Initial commit
This commit is contained in:
commit
0804ed1920
5 changed files with 813 additions and 0 deletions
27
LICENSE
Normal file
27
LICENSE
Normal 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.
|
10
Makefile
Normal file
10
Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
include $(GOROOT)/src/Make.$(GOARCH)
|
||||
|
||||
TARG=jackpal/bencode
|
||||
GOFILES=\
|
||||
decode.go\
|
||||
parse.go\
|
||||
struct.go\
|
||||
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
104
decode.go
Normal file
104
decode.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
// 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 JSON data structure using native Go types: booleans, floats,
|
||||
// strings, arrays, and maps.
|
||||
|
||||
package bencode
|
||||
|
||||
import (
|
||||
"container/vector"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 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 array 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 ParseError. See ParseError documentation for details.
|
||||
func Decode(r io.Reader) (data interface{}, err os.Error) {
|
||||
jb := newDecoder(nil, nil)
|
||||
err = Parse(r, jb)
|
||||
if err == nil {
|
||||
data = jb.Data()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
// A value being constructed.
|
||||
value interface{}
|
||||
// Container entity to flush into. Can be either vector.Vector 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 = new(vector.Vector) }
|
||||
|
||||
func (j *decoder) Map() { j.value = make(map[string]interface{}) }
|
||||
|
||||
func (j *decoder) Elem(i int) Builder {
|
||||
v, ok := j.value.(*vector.Vector)
|
||||
if !ok {
|
||||
v = new(vector.Vector)
|
||||
j.value = v
|
||||
}
|
||||
if v.Len() <= i {
|
||||
v.Resize(i+1, (i+1)*2)
|
||||
}
|
||||
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 *vector.Vector:
|
||||
index := j.index.(int)
|
||||
c.Set(index, j.Data())
|
||||
case map[string]interface{}:
|
||||
index := j.index.(string)
|
||||
c[index] = j.Data()
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value built by this builder.
|
||||
func (j *decoder) Data() interface{} {
|
||||
switch v := j.value.(type) {
|
||||
case *vector.Vector:
|
||||
return v.Data()
|
||||
}
|
||||
return j.value
|
||||
}
|
205
parse.go
Normal file
205
parse.go
Normal file
|
@ -0,0 +1,205 @@
|
|||
// 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"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
io.Reader
|
||||
ReadByte() (c byte, err os.Error)
|
||||
UnreadByte() os.Error
|
||||
}
|
||||
|
||||
// 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,
|
||||
// an array 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 Reader, delim byte) (buf []byte, err os.Error) {
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c == delim {
|
||||
return
|
||||
}
|
||||
if !(c == '-' || (c >= '0' && c <= '9')) {
|
||||
err = os.NewError("expected digit")
|
||||
return
|
||||
}
|
||||
buf = bytes.AddByte(buf, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeInt64(r Reader, delim byte) (data int64, err os.Error) {
|
||||
buf, err := collectInt(r, delim)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data, err = strconv.Atoi64(string(buf))
|
||||
return
|
||||
}
|
||||
|
||||
func decodeString(r Reader) (data string, err os.Error) {
|
||||
len, err := decodeInt64(r, ':')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len < 0 {
|
||||
err = os.NewError("Bad string length")
|
||||
return
|
||||
}
|
||||
var buf = make([]byte, len)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data = string(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func parse(r Reader, build Builder) (err os.Error) {
|
||||
Switch:
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case c >= '1' && c <= '9':
|
||||
// String
|
||||
err = r.UnreadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
str, err := decodeString(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
build.String(str)
|
||||
|
||||
case c == 'd':
|
||||
// dictionary
|
||||
|
||||
build.Map()
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c == 'e' {
|
||||
break
|
||||
}
|
||||
err = r.UnreadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key, err := decodeString(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// TODO: in pendantic mode, check for keys in ascending order.
|
||||
err = parse(r, build.Key(key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case c == 'i':
|
||||
buf, err := collectInt(r, 'e')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
str := string(buf)
|
||||
// If the number is exactly an integer, use that.
|
||||
if i, err := strconv.Atoi64(str); err == nil {
|
||||
build.Int64(i)
|
||||
} else if i, err := strconv.Atoui64(str); err == nil {
|
||||
build.Uint64(i)
|
||||
} else {
|
||||
err = os.NewError("Bad integer")
|
||||
}
|
||||
|
||||
case c == 'l':
|
||||
// array
|
||||
build.Array()
|
||||
n := 0
|
||||
for {
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c == 'e' {
|
||||
break
|
||||
}
|
||||
err = r.UnreadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = parse(r, build.Elem(n))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
default:
|
||||
err = os.NewError("Unexpected character")
|
||||
}
|
||||
|
||||
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 os.Error) {
|
||||
rr, ok := r.(Reader)
|
||||
if !ok {
|
||||
rr = bufio.NewReader(r)
|
||||
}
|
||||
return parse(rr, builder)
|
||||
}
|
467
struct.go
Normal file
467
struct.go
Normal file
|
@ -0,0 +1,467 @@
|
|||
// 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type structBuilder struct {
|
||||
val reflect.Value
|
||||
|
||||
// if map_ != nil, write val to map_[key] on each change
|
||||
map_ *reflect.MapValue
|
||||
key reflect.Value
|
||||
}
|
||||
|
||||
var nobuilder *structBuilder
|
||||
|
||||
func isfloat(v reflect.Value) bool {
|
||||
switch v.(type) {
|
||||
case *reflect.FloatValue, *reflect.Float32Value, *reflect.Float64Value:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setfloat(v reflect.Value, f float64) {
|
||||
switch v := v.(type) {
|
||||
case *reflect.FloatValue:
|
||||
v.Set(float(f))
|
||||
case *reflect.Float32Value:
|
||||
v.Set(float32(f))
|
||||
case *reflect.Float64Value:
|
||||
v.Set(float64(f))
|
||||
}
|
||||
}
|
||||
|
||||
func setint(val reflect.Value, i int64) {
|
||||
switch v := val.(type) {
|
||||
case *reflect.IntValue:
|
||||
v.Set(int(i))
|
||||
case *reflect.Int8Value:
|
||||
v.Set(int8(i))
|
||||
case *reflect.Int16Value:
|
||||
v.Set(int16(i))
|
||||
case *reflect.Int32Value:
|
||||
v.Set(int32(i))
|
||||
case *reflect.Int64Value:
|
||||
v.Set(int64(i))
|
||||
case *reflect.UintValue:
|
||||
v.Set(uint(i))
|
||||
case *reflect.Uint8Value:
|
||||
v.Set(uint8(i))
|
||||
case *reflect.Uint16Value:
|
||||
v.Set(uint16(i))
|
||||
case *reflect.Uint32Value:
|
||||
v.Set(uint32(i))
|
||||
case *reflect.Uint64Value:
|
||||
v.Set(uint64(i))
|
||||
case *reflect.InterfaceValue:
|
||||
v.Set(reflect.NewValue(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 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_ != nil {
|
||||
b.map_.SetElem(b.key, b.val)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *structBuilder) Int64(i int64) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
v := b.val
|
||||
if isfloat(v) {
|
||||
setfloat(v, float64(i))
|
||||
} else {
|
||||
setint(v, i)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *structBuilder) Uint64(i uint64) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
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 v := b.val.(type) {
|
||||
case *reflect.StringValue:
|
||||
v.Set(s)
|
||||
case *reflect.InterfaceValue:
|
||||
v.Set(reflect.NewValue(s))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *structBuilder) Array() {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
if v, ok := b.val.(*reflect.SliceValue); ok {
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.MakeSlice(v.Type().(*reflect.SliceType), 0, 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *structBuilder) Elem(i int) Builder {
|
||||
if b == nil || i < 0 {
|
||||
return nobuilder
|
||||
}
|
||||
switch v := b.val.(type) {
|
||||
case *reflect.ArrayValue:
|
||||
if i < v.Len() {
|
||||
return &structBuilder{val: v.Elem(i)}
|
||||
}
|
||||
case *reflect.SliceValue:
|
||||
if i >= v.Cap() {
|
||||
n := v.Cap()
|
||||
if n < 8 {
|
||||
n = 8
|
||||
}
|
||||
for n <= i {
|
||||
n *= 2
|
||||
}
|
||||
nv := reflect.MakeSlice(v.Type().(*reflect.SliceType), v.Len(), n)
|
||||
reflect.ArrayCopy(nv, v)
|
||||
v.Set(nv)
|
||||
}
|
||||
if v.Len() <= i && i < v.Cap() {
|
||||
v.SetLen(i + 1)
|
||||
}
|
||||
if i < v.Len() {
|
||||
return &structBuilder{val: v.Elem(i)}
|
||||
}
|
||||
}
|
||||
return nobuilder
|
||||
}
|
||||
|
||||
func (b *structBuilder) Map() {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
if v, ok := b.val.(*reflect.PtrValue); ok && v.IsNil() {
|
||||
if v.IsNil() {
|
||||
v.PointTo(reflect.MakeZero(v.Type().(*reflect.PtrType).Elem()))
|
||||
b.Flush()
|
||||
}
|
||||
b.map_ = nil
|
||||
b.val = v.Elem()
|
||||
}
|
||||
if v, ok := b.val.(*reflect.MapValue); ok && v.IsNil() {
|
||||
v.Set(reflect.MakeMap(v.Type().(*reflect.MapType)))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *structBuilder) Key(k string) Builder {
|
||||
if b == nil {
|
||||
return nobuilder
|
||||
}
|
||||
switch v := reflect.Indirect(b.val).(type) {
|
||||
case *reflect.StructValue:
|
||||
t := v.Type().(*reflect.StructType)
|
||||
// Case-insensitive field lookup.
|
||||
k = strings.ToLower(k)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if strings.ToLower(field.Tag) == k ||
|
||||
strings.ToLower(field.Name) == k {
|
||||
return &structBuilder{val: v.Field(i)}
|
||||
}
|
||||
}
|
||||
case *reflect.MapValue:
|
||||
t := v.Type().(*reflect.MapType)
|
||||
if t.Key() != reflect.Typeof(k) {
|
||||
break
|
||||
}
|
||||
key := reflect.NewValue(k)
|
||||
elem := v.Elem(key)
|
||||
if elem == nil {
|
||||
v.SetElem(key, reflect.MakeZero(t.Elem()))
|
||||
elem = v.Elem(key)
|
||||
}
|
||||
return &structBuilder{val: elem, map_: v, key: key}
|
||||
}
|
||||
return nobuilder
|
||||
}
|
||||
|
||||
// Unmarshal parses the bencode syntax string s 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.
|
||||
//
|
||||
// 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 os.Error) {
|
||||
err = UnmarshalValue(r, reflect.NewValue(val))
|
||||
return
|
||||
}
|
||||
|
||||
// This API is public primarily to make testing easier, but it is available if you
|
||||
// have a use for it.
|
||||
|
||||
func UnmarshalValue(r io.Reader, v reflect.Value) (err os.Error) {
|
||||
var b *structBuilder
|
||||
|
||||
// If val is a pointer to a slice, we append to the slice.
|
||||
if ptr, ok := v.(*reflect.PtrValue); ok {
|
||||
if slice, ok := ptr.Elem().(*reflect.SliceValue); ok {
|
||||
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) String() string {
|
||||
return "bencode cannot encode value of type " + e.T.String()
|
||||
}
|
||||
|
||||
func writeArrayOrSlice(w io.Writer, val reflect.ArrayOrSliceValue) (err os.Error) {
|
||||
_, err = fmt.Fprint(w, "l")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
if err := writeValue(w, val.Elem(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 os.Error) {
|
||||
sort.Sort(svList)
|
||||
|
||||
for _, sv := range (svList) {
|
||||
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.MapValue) (err os.Error) {
|
||||
key := val.Type().(*reflect.MapType).Key()
|
||||
if _, ok := key.(*reflect.StringType); !ok {
|
||||
return &MarshalError{val.Type()}
|
||||
}
|
||||
_, err = fmt.Fprint(w, "d")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keys := val.Keys()
|
||||
|
||||
// Sort keys
|
||||
|
||||
svList := make(StringValueArray, len(keys))
|
||||
for i, key := range (keys) {
|
||||
svList[i].key = key.(*reflect.StringValue).Get()
|
||||
svList[i].value = val.Elem(key)
|
||||
}
|
||||
|
||||
err = writeSVList(w, svList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = fmt.Fprint(w, "e")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeStruct(w io.Writer, val *reflect.StructValue) (err os.Error) {
|
||||
_, err = fmt.Fprint(w, "d")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
typ := val.Type().(*reflect.StructType)
|
||||
|
||||
numFields := val.NumField()
|
||||
svList := make(StringValueArray, numFields)
|
||||
|
||||
for i := 0; i < numFields; i++ {
|
||||
field := typ.Field(i)
|
||||
key := field.Name
|
||||
if len(field.Tag) > 0 {
|
||||
key = field.Tag
|
||||
}
|
||||
svList[i].key = key
|
||||
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 os.Error) {
|
||||
if val == nil {
|
||||
err = os.NewError("Can't write null value")
|
||||
return
|
||||
}
|
||||
|
||||
switch v := val.(type) {
|
||||
case *reflect.StringValue:
|
||||
s := v.Get()
|
||||
_, err = fmt.Fprintf(w, "%d:%s", len(s), s)
|
||||
case *reflect.IntValue:
|
||||
_, err = fmt.Fprintf(w, "i%de", v.Get())
|
||||
case *reflect.UintValue:
|
||||
_, err = fmt.Fprintf(w, "i%de", v.Get())
|
||||
case *reflect.Int64Value:
|
||||
_, err = fmt.Fprintf(w, "i%de", v.Get())
|
||||
case *reflect.Uint64Value:
|
||||
_, err = fmt.Fprintf(w, "i%de", v.Get())
|
||||
case *reflect.ArrayValue:
|
||||
err = writeArrayOrSlice(w, v)
|
||||
case *reflect.SliceValue:
|
||||
err = writeArrayOrSlice(w, v)
|
||||
case *reflect.MapValue:
|
||||
err = writeMap(w, v)
|
||||
case *reflect.StructValue:
|
||||
err = writeStruct(w, v)
|
||||
case *reflect.InterfaceValue:
|
||||
err = writeValue(w, v.Elem())
|
||||
default:
|
||||
err = &MarshalError{val.Type()}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Marshal(w io.Writer, val interface{}) os.Error {
|
||||
return writeValue(w, reflect.NewValue(val))
|
||||
}
|
Loading…
Reference in a new issue