move bencode to subdirectory

making room for other packages
This commit is contained in:
Vincent Batts 2014-08-14 14:27:17 -04:00
parent c104c8e258
commit 0eb8ce1253
7 changed files with 3 additions and 0 deletions

27
bencode/LICENSE Normal file
View file

@ -0,0 +1,27 @@
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1
bencode/README.md Normal file
View file

@ -0,0 +1 @@
this is a copy from code.google.com/p/bencode-go

266
bencode/bencode_test.go Normal file
View file

@ -0,0 +1,266 @@
package bencode
import (
"bytes"
"errors"
"fmt"
"reflect"
"testing"
)
type any interface{}
func checkMarshal(expected string, data any) (err error) {
var b bytes.Buffer
if err = Marshal(&b, data); err != nil {
return
}
s := b.String()
if expected != s {
err = errors.New(fmt.Sprintf("Expected %s got %s", expected, s))
return
}
return
}
func check(expected string, data any) (err error) {
if err = checkMarshal(expected, data); err != nil {
return
}
b2 := bytes.NewBufferString(expected)
val, err := Decode(b2)
if err != nil {
err = errors.New(fmt.Sprint("Failed decoding ", expected, " ", err))
return
}
if err = checkFuzzyEqual(data, val); err != nil {
return
}
return
}
func checkFuzzyEqual(a any, b any) (err error) {
if !fuzzyEqual(a, b) {
err = errors.New(fmt.Sprint(a, " != ", b,
": ", reflect.ValueOf(a), "!=", reflect.ValueOf(b)))
}
return
}
func fuzzyEqual(a, b any) bool {
return fuzzyEqualValue(reflect.ValueOf(a), reflect.ValueOf(b))
}
func checkFuzzyEqualValue(a, b reflect.Value) (err error) {
if !fuzzyEqualValue(a, b) {
err = fmt.Errorf("Wanted %v(%v) got %v(%v)", a, a.Interface(), b, b.Interface())
}
return
}
func fuzzyEqualInt64(a int64, b reflect.Value) bool {
switch vb := b; vb.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return a == (vb.Int())
default:
return false
}
return false
}
func fuzzyEqualArrayOrSlice(va reflect.Value, b reflect.Value) bool {
switch vb := b; vb.Kind() {
case reflect.Array:
return fuzzyEqualArrayOrSlice2(va, vb)
case reflect.Slice:
return fuzzyEqualArrayOrSlice2(va, vb)
default:
return false
}
return false
}
func deInterface(a reflect.Value) reflect.Value {
switch va := a; va.Kind() {
case reflect.Interface:
return va.Elem()
}
return a
}
func fuzzyEqualArrayOrSlice2(a reflect.Value, b reflect.Value) bool {
if a.Len() != b.Len() {
return false
}
for i := 0; i < a.Len(); i++ {
ea := deInterface(a.Index(i))
eb := deInterface(b.Index(i))
if !fuzzyEqualValue(ea, eb) {
return false
}
}
return true
}
func fuzzyEqualMap(a reflect.Value, b reflect.Value) bool {
key := a.Type().Key()
if key.Kind() != reflect.String {
return false
}
key = b.Type().Key()
if key.Kind() != reflect.String {
return false
}
aKeys, bKeys := a.MapKeys(), b.MapKeys()
if len(aKeys) != len(bKeys) {
return false
}
for _, k := range aKeys {
if !fuzzyEqualValue(a.MapIndex(k), b.MapIndex(k)) {
return false
}
}
return true
}
func fuzzyEqualStruct(a reflect.Value, b reflect.Value) bool {
numA, numB := a.NumField(), b.NumField()
if numA != numB {
return false
}
for i := 0; i < numA; i++ {
if !fuzzyEqualValue(a.Field(i), b.Field(i)) {
return false
}
}
return true
}
func fuzzyEqualValue(a, b reflect.Value) bool {
switch va := a; va.Kind() {
case reflect.String:
switch vb := b; vb.Kind() {
case reflect.String:
return va.String() == vb.String()
default:
return false
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fuzzyEqualInt64(va.Int(), b)
case reflect.Array:
return fuzzyEqualArrayOrSlice(va, b)
case reflect.Slice:
return fuzzyEqualArrayOrSlice(va, b)
case reflect.Map:
switch vb := b; vb.Kind() {
case reflect.Map:
return fuzzyEqualMap(va, vb)
default:
return false
}
case reflect.Struct:
switch vb := b; vb.Kind() {
case reflect.Struct:
return fuzzyEqualStruct(va, vb)
default:
return false
}
case reflect.Interface:
switch vb := b; vb.Kind() {
case reflect.Interface:
return fuzzyEqualValue(va.Elem(), vb.Elem())
default:
return false
}
default:
return false
}
return false
}
func checkUnmarshal(expected string, data any) (err error) {
if err = checkMarshal(expected, data); err != nil {
return
}
dataValue := reflect.ValueOf(data)
newOne := reflect.New(reflect.TypeOf(data))
buf := bytes.NewBufferString(expected)
if err = unmarshalValue(buf, newOne); err != nil {
return
}
if err = checkFuzzyEqualValue(dataValue, newOne.Elem()); err != nil {
return
}
return
}
type SVPair struct {
s string
v any
}
func TestDecode(t *testing.T) {
tests := []SVPair{
SVPair{"i0e", int64(0)},
SVPair{"i0e", 0},
SVPair{"i100e", 100},
SVPair{"i-100e", -100},
SVPair{"1:a", "a"},
SVPair{"2:a\"", "a\""},
SVPair{"11:0123456789a", "0123456789a"},
SVPair{"le", []int64{}},
SVPair{"li1ei2ee", []int{1, 2}},
SVPair{"l3:abc3:defe", []string{"abc", "def"}},
SVPair{"li42e3:abce", []any{42, "abc"}},
SVPair{"de", map[string]any{}},
SVPair{"d3:cati1e3:dogi2ee", map[string]any{"cat": 1, "dog": 2}},
}
for _, sv := range tests {
if err := check(sv.s, sv.v); err != nil {
t.Error(err.Error())
}
}
}
type structA struct {
A int "a"
B string `example:"data" bencode:"b"`
C string `example:"data2" bencode:"sea monster"`
}
func TestUnmarshal(t *testing.T) {
type structNested struct {
T string "t"
Y string "y"
Q string "q"
A map[string]string "a"
}
innerDict := map[string]string{"id": "abcdefghij0123456789"}
nestedDictionary := structNested{"aa", "q", "ping", innerDict}
tests := []SVPair{
SVPair{"i100e", 100},
SVPair{"i-100e", -100},
SVPair{"1:a", "a"},
SVPair{"2:a\"", "a\""},
SVPair{"11:0123456789a", "0123456789a"},
SVPair{"le", []int64{}},
SVPair{"li1ei2ee", []int{1, 2}},
SVPair{"l3:abc3:defe", []string{"abc", "def"}},
SVPair{"li42e3:abce", []any{42, "abc"}},
SVPair{"de", map[string]any{}},
SVPair{"d3:cati1e3:dogi2ee", map[string]any{"cat": 1, "dog": 2}},
SVPair{"d1:ai10e1:b3:foo11:sea monster3:bare", structA{10, "foo", "bar"}},
SVPair{"d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe", nestedDictionary},
}
for _, sv := range tests {
if err := checkUnmarshal(sv.s, sv.v); err != nil {
t.Error(err.Error())
}
}
}

106
bencode/decode.go Normal file
View file

@ -0,0 +1,106 @@
// 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.
// Represents bencode data structure using native Go types: booleans, floats,
// strings, slices, and maps.
package bencode
import (
"io"
)
// Decode a bencode stream
// Decode parses the stream r and returns the
// generic bencode object representation. The object representation is a tree
// of Go data types. The data return value may be one of string,
// int64, uint64, []interface{} or map[string]interface{}. The slice and map
// elements may in turn contain any of the types listed above and so on.
//
// If Decode encounters a syntax error, it returns with err set to an
// instance of Error.
func Decode(r io.Reader) (data interface{}, err error) {
jb := newDecoder(nil, nil)
err = parse(r, jb)
if err == nil {
data = jb.Copy()
}
return
}
type decoder struct {
// A value being constructed.
value interface{}
// Container entity to flush into. Can be either []interface{} or
// map[string]interface{}.
container interface{}
// The index into the container interface. Either int or string.
index interface{}
}
func newDecoder(container interface{}, key interface{}) *decoder {
return &decoder{container: container, index: key}
}
func (j *decoder) Int64(i int64) { j.value = int64(i) }
func (j *decoder) Uint64(i uint64) { j.value = uint64(i) }
func (j *decoder) Float64(f float64) { j.value = float64(f) }
func (j *decoder) String(s string) { j.value = s }
func (j *decoder) Bool(b bool) { j.value = b }
func (j *decoder) Null() { j.value = nil }
func (j *decoder) Array() { j.value = make([]interface{}, 0, 8) }
func (j *decoder) Map() { j.value = make(map[string]interface{}) }
func (j *decoder) Elem(i int) builder {
v, ok := j.value.([]interface{})
if !ok {
v = make([]interface{}, 0, 8)
j.value = v
}
/* XXX There is a bug in here somewhere, but append() works fine.
lens := len(v)
if cap(v) <= lens {
news := make([]interface{}, 0, lens*2)
copy(news, j.value.([]interface{}))
v = news
}
v = v[0 : lens+1]
*/
v = append(v, nil)
j.value = v
return newDecoder(v, i)
}
func (j *decoder) Key(s string) builder {
m, ok := j.value.(map[string]interface{})
if !ok {
m = make(map[string]interface{})
j.value = m
}
return newDecoder(m, s)
}
func (j *decoder) Flush() {
switch c := j.container.(type) {
case []interface{}:
index := j.index.(int)
c[index] = j.Copy()
case map[string]interface{}:
index := j.index.(string)
c[index] = j.Copy()
}
}
// Get the value built by this builder.
func (j *decoder) Copy() interface{} {
return j.value
}

200
bencode/parse.go Normal file
View file

@ -0,0 +1,200 @@
// 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"
"errors"
"fmt"
"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.
// 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,
// a slice element or a map key).
//
// 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
// to the bencode parser. It gives clients full control over the
// eventual representation returned by the parser.
type builder interface {
// Set value
Int64(i int64)
Uint64(i uint64)
String(s string)
Array()
Map()
// Create sub-Builders
Elem(i int) builder
Key(s string) builder
// Flush changes to parent builder if necessary.
Flush()
}
func collectInt(r *bufio.Reader, delim byte) (buf []byte, err error) {
for {
var c byte
c, err = r.ReadByte()
if err != nil {
return
}
if c == delim {
return
}
if !(c == '-' || (c >= '0' && c <= '9')) {
err = errors.New("expected digit")
return
}
buf = append(buf, c)
}
return
}
func decodeInt64(r *bufio.Reader, delim byte) (data int64, err error) {
buf, err := collectInt(r, delim)
if err != nil {
return
}
data, err = strconv.ParseInt(string(buf), 10, 64)
return
}
func decodeString(r *bufio.Reader) (data string, err error) {
length, err := decodeInt64(r, ':')
if err != nil {
return
}
if length < 0 {
err = errors.New("Bad string length")
return
}
var buf = make([]byte, length)
_, err = io.ReadFull(r, buf)
if err != nil {
return
}
data = string(buf)
return
}
func parseFromReader(r *bufio.Reader, build builder) (err error) {
c, err := r.ReadByte()
if err != nil {
goto exit
}
switch {
case c >= '0' && c <= '9':
// String
err = r.UnreadByte()
if err != nil {
goto exit
}
var str string
str, err = decodeString(r)
if err != nil {
goto exit
}
build.String(str)
case c == 'd':
// dictionary
build.Map()
for {
c, err = r.ReadByte()
if err != nil {
goto exit
}
if c == 'e' {
break
}
err = r.UnreadByte()
if err != nil {
goto exit
}
var key string
key, err = decodeString(r)
if err != nil {
goto exit
}
// TODO: in pendantic mode, check for keys in ascending order.
err = parse(r, build.Key(key))
if err != nil {
goto exit
}
}
case c == 'i':
var buf []byte
buf, err = collectInt(r, 'e')
if err != nil {
goto exit
}
var str string
var i int64
var i2 uint64
str = string(buf)
// If the number is exactly an integer, use that.
if i, err = strconv.ParseInt(str, 10, 64); err == nil {
build.Int64(i)
} else if i2, err = strconv.ParseUint(str, 10, 64); err == nil {
build.Uint64(i2)
} else {
err = errors.New("Bad integer")
}
case c == 'l':
// array
build.Array()
n := 0
for {
c, err = r.ReadByte()
if err != nil {
goto exit
}
if c == 'e' {
break
}
err = r.UnreadByte()
if err != nil {
goto exit
}
err = parseFromReader(r, build.Elem(n))
if err != nil {
goto exit
}
n++
}
default:
err = errors.New(fmt.Sprintf("Unexpected character: '%v'", c))
}
exit:
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)
}

540
bencode/struct.go Normal file
View file

@ -0,0 +1,540 @@
// 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.
// Marshalling and unmarshalling of
// bit torrent bencode data into Go structs using reflection.
//
// Based upon the standard Go language JSON package.
package bencode
import (
"errors"
"fmt"
"io"
"reflect"
"sort"
"strings"
)
type structBuilder struct {
val reflect.Value
// if map_ != nil, write val to map_[key] on each change
map_ reflect.Value
key reflect.Value
}
var nobuilder *structBuilder
func isfloat(v reflect.Value) bool {
switch v.Kind() {
case reflect.Float32, reflect.Float64:
return true
}
return false
}
func setfloat(v reflect.Value, f float64) {
switch v.Kind() {
case reflect.Float32, reflect.Float64:
v.SetFloat(f)
}
}
func setint(val reflect.Value, i int64) {
switch v := val; v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v.SetInt(int64(i))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v.SetUint(uint64(i))
case reflect.Interface:
v.Set(reflect.ValueOf(i))
default:
panic("setint called for bogus type: " + val.Kind().String())
}
}
// If updating b.val is not enough to update the original,
// copy a changed b.val out to the original.
func (b *structBuilder) Flush() {
if b == nil {
return
}
if b.map_.IsValid() {
b.map_.SetMapIndex(b.key, b.val)
}
}
func (b *structBuilder) Int64(i int64) {
if b == nil {
return
}
if !b.val.CanSet() {
x := 0
b.val = reflect.ValueOf(&x).Elem()
}
v := b.val
if isfloat(v) {
setfloat(v, float64(i))
} else {
setint(v, i)
}
}
func (b *structBuilder) Uint64(i uint64) {
if b == nil {
return
}
if !b.val.CanSet() {
x := uint64(0)
b.val = reflect.ValueOf(&x).Elem()
}
v := b.val
if isfloat(v) {
setfloat(v, float64(i))
} else {
setint(v, int64(i))
}
}
func (b *structBuilder) Float64(f float64) {
if b == nil {
return
}
if !b.val.CanSet() {
x := float64(0)
b.val = reflect.ValueOf(&x).Elem()
}
v := b.val
if isfloat(v) {
setfloat(v, f)
} else {
setint(v, int64(f))
}
}
func (b *structBuilder) String(s string) {
if b == nil {
return
}
switch b.val.Kind() {
case reflect.String:
if !b.val.CanSet() {
x := ""
b.val = reflect.ValueOf(&x).Elem()
}
b.val.SetString(s)
case reflect.Interface:
b.val.Set(reflect.ValueOf(s))
}
}
func (b *structBuilder) Array() {
if b == nil {
return
}
if v := b.val; v.Kind() == reflect.Slice {
if v.IsNil() {
v.Set(reflect.MakeSlice(v.Type(), 0, 8))
}
}
}
func (b *structBuilder) Elem(i int) builder {
if b == nil || i < 0 {
return nobuilder
}
switch v := b.val; v.Kind() {
case reflect.Array:
if i < v.Len() {
return &structBuilder{val: v.Index(i)}
}
case reflect.Slice:
if i >= v.Cap() {
n := v.Cap()
if n < 8 {
n = 8
}
for n <= i {
n *= 2
}
nv := reflect.MakeSlice(v.Type(), v.Len(), n)
reflect.Copy(nv, v)
v.Set(nv)
}
if v.Len() <= i && i < v.Cap() {
v.SetLen(i + 1)
}
if i < v.Len() {
return &structBuilder{val: v.Index(i)}
}
}
return nobuilder
}
func (b *structBuilder) Map() {
if b == nil {
return
}
if v := b.val; v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.Zero(v.Type().Elem()).Addr())
b.Flush()
}
b.map_ = reflect.Value{}
b.val = v.Elem()
}
if v := b.val; v.Kind() == reflect.Map && v.IsNil() {
v.Set(reflect.MakeMap(v.Type()))
}
}
func (b *structBuilder) Key(k string) builder {
if b == nil {
return nobuilder
}
switch v := reflect.Indirect(b.val); v.Kind() {
case reflect.Struct:
t := v.Type()
// Case-insensitive field lookup.
k = strings.ToLower(k)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
key := bencodeKey(field)
if strings.ToLower(key) == k ||
strings.ToLower(field.Name) == k {
return &structBuilder{val: v.Field(i)}
}
}
case reflect.Map:
t := v.Type()
if t.Key() != reflect.TypeOf(k) {
break
}
key := reflect.ValueOf(k)
elem := v.MapIndex(key)
if !elem.IsValid() {
v.SetMapIndex(key, reflect.Zero(t.Elem()))
elem = v.MapIndex(key)
}
return &structBuilder{val: elem, map_: v, key: key}
}
return nobuilder
}
// Unmarshal reads and parses the bencode syntax data from r and fills in
// an arbitrary struct or slice pointed at by val.
// It uses the reflect package to assign to fields
// and arrays embedded in val. Well-formed data that does not fit
// into the struct is discarded.
//
// For example, given these definitions:
//
// type Email struct {
// Where string;
// Addr string;
// }
//
// type Result struct {
// Name string;
// Phone string;
// Email []Email
// }
//
// var r = Result{ "name", "phone", nil }
//
// unmarshalling the bencode syntax string
//
// "d5:emailld5:where4:home4:addr15:gre@example.come\
// d5:where4:work4:addr12:gre@work.comee4:name14:Gr\
// ace R. Emlin7:address15:123 Main Streete"
//
// via Unmarshal(s, &r) is equivalent to assigning
//
// r = Result{
// "Grace R. Emlin", // name
// "phone", // no phone given
// []Email{
// Email{ "home", "gre@example.com" },
// Email{ "work", "gre@work.com" }
// }
// }
//
// Note that the field r.Phone has not been modified and
// that the bencode field "address" was discarded.
//
// Because Unmarshal uses the reflect package, it can only
// assign to upper case fields. Unmarshal uses a case-insensitive
// comparison to match bencode field names to struct field names.
//
// If you provide a tag string for a struct member, the tag string
// will be used as the bencode dictionary key for that member.
// Bencode undestands both the original single-string and updated
// list-of-key-value-pairs tag string syntax. The list-of-key-value
// pairs syntax is assumed, with a fallback to the original single-string
// syntax. The key for bencode values is bencode.
//
// To unmarshal a top-level bencode array, pass in a pointer to an empty
// slice of the correct type.
//
func Unmarshal(r io.Reader, val interface{}) (err error) {
// If e represents a value, the answer won't get back to the
// caller. Make sure it's a pointer.
if reflect.TypeOf(val).Kind() != reflect.Ptr {
err = errors.New("Attempt to unmarshal into a non-pointer")
return
}
err = unmarshalValue(r, reflect.Indirect(reflect.ValueOf(val)))
return
}
func unmarshalValue(r io.Reader, v reflect.Value) (err error) {
var b *structBuilder
// XXX: Decide if the extra codnitions are needed. Affect map?
if ptr := v; ptr.Kind() == reflect.Ptr {
if slice := ptr.Elem(); slice.Kind() == reflect.Slice || slice.Kind() == reflect.Int || slice.Kind() == reflect.String {
b = &structBuilder{val: slice}
}
}
if b == nil {
b = &structBuilder{val: v}
}
err = parse(r, b)
return
}
type MarshalError struct {
T reflect.Type
}
func (e *MarshalError) Error() string {
return "bencode cannot encode value of type " + e.T.String()
}
func writeArrayOrSlice(w io.Writer, val reflect.Value) (err error) {
_, err = fmt.Fprint(w, "l")
if err != nil {
return
}
for i := 0; i < val.Len(); i++ {
if err := writeValue(w, val.Index(i)); err != nil {
return err
}
}
_, err = fmt.Fprint(w, "e")
if err != nil {
return
}
return nil
}
type stringValue struct {
key string
value reflect.Value
}
type stringValueArray []stringValue
// Satisfy sort.Interface
func (a stringValueArray) Len() int { return len(a) }
func (a stringValueArray) Less(i, j int) bool { return a[i].key < a[j].key }
func (a stringValueArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func writeSVList(w io.Writer, svList stringValueArray) (err error) {
sort.Sort(svList)
for _, sv := range svList {
if isValueNil(sv.value) {
continue // Skip null values
}
s := sv.key
_, err = fmt.Fprintf(w, "%d:%s", len(s), s)
if err != nil {
return
}
if err = writeValue(w, sv.value); err != nil {
return
}
}
return
}
func writeMap(w io.Writer, val reflect.Value) (err error) {
key := val.Type().Key()
if key.Kind() != reflect.String {
return &MarshalError{val.Type()}
}
_, err = fmt.Fprint(w, "d")
if err != nil {
return
}
keys := val.MapKeys()
// Sort keys
svList := make(stringValueArray, len(keys))
for i, key := range keys {
svList[i].key = key.String()
svList[i].value = val.MapIndex(key)
}
err = writeSVList(w, svList)
if err != nil {
return
}
_, err = fmt.Fprint(w, "e")
if err != nil {
return
}
return
}
func bencodeKey(field reflect.StructField) (key string) {
key = field.Name
tag := field.Tag
if len(tag) > 0 {
// Backwards compatability
// If there's a bencode key/value entry in the tag, use it.
key = tag.Get("bencode")
if len(key) == 0 {
// If there is no ":" in the tag, assume it is an old-style tag.
stringTag := string(tag)
if !strings.Contains(stringTag, ":") {
key = stringTag
}
}
}
return
}
func writeStruct(w io.Writer, val reflect.Value) (err error) {
_, err = fmt.Fprint(w, "d")
if err != nil {
return
}
typ := val.Type()
numFields := val.NumField()
svList := make(stringValueArray, numFields)
for i := 0; i < numFields; i++ {
field := typ.Field(i)
svList[i].key = bencodeKey(field)
svList[i].value = val.Field(i)
}
err = writeSVList(w, svList)
if err != nil {
return
}
_, err = fmt.Fprint(w, "e")
if err != nil {
return
}
return
}
func writeValue(w io.Writer, val reflect.Value) (err error) {
if !val.IsValid() {
err = errors.New("Can't write null value")
return
}
switch v := val; v.Kind() {
case reflect.String:
s := v.String()
_, err = fmt.Fprintf(w, "%d:%s", len(s), s)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
_, err = fmt.Fprintf(w, "i%de", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
_, err = fmt.Fprintf(w, "i%de", v.Uint())
case reflect.Array:
err = writeArrayOrSlice(w, v)
case reflect.Slice:
err = writeArrayOrSlice(w, v)
case reflect.Map:
err = writeMap(w, v)
case reflect.Struct:
err = writeStruct(w, v)
case reflect.Interface:
err = writeValue(w, v.Elem())
default:
err = &MarshalError{val.Type()}
}
return
}
func isValueNil(val reflect.Value) bool {
if !val.IsValid() {
return true
}
switch v := val; v.Kind() {
case reflect.Interface:
return isValueNil(v.Elem())
default:
return false
}
return false
}
// Marshal writes the bencode encoding of val to w.
//
// Marshal traverses the value v recursively.
//
// Marshal uses the following type-dependent encodings:
//
// Floating point, integer, and Number values encode as bencode numbers.
//
// String values encode as bencode strings.
//
// Array and slice values encode as bencode arrays.
//
// Struct values encode as bencode maps. Each exported struct field
// becomes a member of the object.
// The object's default key string is the struct field name
// but can be specified in the struct field's tag value. The text of
// the struct field's tag value is the key name. Examples:
//
// // Field appears in bencode as key "Field".
// Field int
//
// // Field appears in bencode as key "myName".
// Field int "myName"
//
// Anonymous struct fields are ignored.
//
// Map values encode as bencode objects.
// The map's key type must be string; the object keys are used directly
// as map keys.
//
// Boolean, Pointer, Interface, Channel, complex, and function values cannot
// be encoded in bencode.
// Attempting to encode such a value causes Marshal to return
// a MarshalError.
//
// Bencode cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(w io.Writer, val interface{}) error {
// TODO match other encoders, like encoding/json ...
// func Marshal(v interface{}) ([]byte, error)
return writeValue(w, reflect.ValueOf(val))
}