Fixed skipping, better docs

This commit is contained in:
Emery Hemingway 2013-12-05 14:14:55 -05:00
parent 1dcd7c791f
commit 011343cff3
5 changed files with 123 additions and 65 deletions

View File

@ -39,6 +39,24 @@ var tests = []test{
{in: "d3:cati1e3:dogi2ee", ptr: new(map[string]int), out: map[string]int{"cat": 1, "dog": 2}},
}
var afs = []byte("d18:availableFunctionsd18:AdminLog_subscribed4:filed8:requiredi0e4:type6:Stringe5:leveld8:requiredi0e4:type6:Stringe4:lined8:requiredi0e4:type3:Intee20:AdminLog_unsubscribed8:streamIdd8:requiredi1e4:type6:Stringee18:Admin_asyncEnabledde24:Admin_availableFunctionsd4:paged8:requiredi0e4:type3:Intee34:InterfaceController_disconnectPeerd6:pubkeyd8:requiredi1e4:type6:Stringee29:InterfaceController_peerStatsd4:paged8:requiredi0e4:type3:Intee17:SwitchPinger_pingd4:datad8:requiredi0e4:type6:Stringe4:pathd8:requiredi1e4:type6:Stringe7:timeoutd8:requiredi0e4:type3:Intee16:UDPInterface_newd11:bindAddressd8:requiredi0e4:type6:Stringeee4:morei1e4:txid8:c37b0faae")
type outer struct {
More bool `bencode:"more"`
Txid string `bencode:"txid"`
}
func TestNesting(t *testing.T) {
o := new(outer)
err := Unmarshal(afs, o)
if err != nil {
t.Fatal("error unmarshaling nested struct", err)
}
if o.Txid != "c37b0faa" {
t.Errorf("got txid %q", o.Txid)
}
}
func TestMarshal(t *testing.T) {
buf := new(bytes.Buffer)
enc := NewEncoder(buf)
@ -104,7 +122,7 @@ func BenchmarkUnmarshal(b *testing.B) {
x := new(benchmarkStruct)
var err error
for i := 0; i < b.N; i++ {
if err = Unmarshal(benchmarkTest, x); err != nil {
if err = Unmarshal(benchmarkTest, *x); err != nil {
b.Fatal(err.Error())
}
}

View File

@ -193,18 +193,26 @@ func (d *decodeState) saveError(err error) {
}
// skip reads d.data until it hits the given op code
func (d *decodeState) skip(seekOp int) {
func (d *decodeState) skip() { //seekOp int) {
var skipScan scanner
skipScan.reset()
skipScan.step = d.scan.step
d.scan.step = stateEndValue
var op int
for {
op := d.scan.step(&d.scan, int(d.data[d.off]))
d.off++
op = skipScan.step(&skipScan, int(d.data[d.off]))
switch op {
case seekOp:
case scanEnd:
return
case scanError:
d.error(d.scan.err)
case scanEnd:
d.error(errPhase)
d.error(skipScan.err)
}
if d.off > len(d.data) {
d.error(errors.New("reached end of data"))
}
d.off++
}
}
@ -214,34 +222,16 @@ func (d *decodeState) value(v reflect.Value) {
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
c := int(d.data[d.off])
d.off++
op := d.scan.step(&d.scan, c)
var op int
if !v.IsValid() {
// skip beyond this value
switch op {
case scanBeginStringLen:
d.skip(scanEndString)
case scanBeginInteger:
d.skip(scanEndInteger)
case scanBeginList:
d.skip(scanEndList)
case scanBeginDict:
d.skip(scanEndDict)
case scanError:
d.error(d.scan.err)
default:
d.error(errPhase)
}
d.skip()
return
}
op = d.scan.step(&d.scan, int(d.data[d.off]))
d.off++
switch op {
case scanBeginStringLen:
d.string(v)
@ -330,12 +320,6 @@ func (d *decodeState) integer(v reflect.Value) {
s := string(d.readInteger())
switch k {
default:
d.error(&UnmarshalTypeError{"number " + s, v.Type()})
case reflect.String:
v.SetString(s)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
@ -353,9 +337,26 @@ func (d *decodeState) integer(v reflect.Value) {
case reflect.Float32, reflect.Float64:
n, err := strconv.ParseFloat(s, v.Type().Bits())
if err != nil || v.OverflowFloat(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
d.saveError(&UnmarshalTypeError{"integer " + s, v.Type()})
}
v.SetFloat(n)
case reflect.String:
v.SetString(s)
case reflect.Bool:
switch s {
case "1", "true":
v.SetBool(true)
return
case "0":
v.SetBool(true)
return
}
fallthrough
default:
d.error(&UnmarshalTypeError{"integer " + s, v.Type()})
}
}
@ -580,7 +581,7 @@ Read:
c = int(d.data[d.off])
d.off++
switch d.scan.step(&d.scan, c) {
switch op := d.scan.step(&d.scan, c); op {
case scanEndDict:
break Read
case scanBeginKeyLen, scanParseKeyLen, scanParseKey:

View File

@ -45,40 +45,80 @@ func (enc *Encoder) Encode(v interface{}) error {
// Marshal returns a bencoded form of x.
//
// Marshal traverses the value x recursively.
// Marshal traverses the value v recursively using the the following
// type-dependent encodings:
//
// Marshal uses the following type-dependent encodings:
// Integer types encode as bencode integers.
//
// Floating point, integer, and Number values encode as bencode numbers.
// String and []byte values encode as bencode strings.
//
// String values encode as bencode strings.
// Struct values encode as bencode dictionaries. Each exported struct
// field becomes a member of the object unless
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
// The empty values are false, 0, any nil pointer or interface value,
// and any array, slice, string, or map with zero length.
// The values default key string is the struct field name but can be
// specified in the struct field's tag value. The "bencode" key in the
// struct field's tag value is the key name, followed by an optional
// comma and options. Examples:
//
// Array and slice values encode as bencode arrays.
// // Field is ignored by this package.
// Field int `bencode:"-"`
//
// 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 appears in bencode dictionaries as "6:myName".
// Field int `bencode:"myName"`
//
// Anonymous struct fields are ignored.
// // Field appears in bencode dictionaries as "6:myName" and
// // will be omitted if it's value is empty,
// // as defined above.
// Field int `bencode"myName,omitEmpty"`
//
// // Field appears in bencode dictionaries as "5:Field" (the default),
// // but the field is skipped if empty.
// // Note the leading comma.
// Field int `bencode:",omitempty"`
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
// underscores and slashes.
//
// Anonymous struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
// as described in the next paragraph.
// An anonymous struct field with a name given in its bencode tag is treated as
// having that name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for bencode when
// deciding which field to marshal or unmarshal. If there are
// multiple fields at the same level, and that level is the least
// nested (and would therefore be the nesting level selected by the
// usual Go rules), the following extra rules apply:
//
// 1) Of those fields, if any are bencode-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Handling of anonymous struct fields is new in Go 1.1.
// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of
// an anonymous struct field in both current and earlier versions, give the field
// a bencode tag of "-".
//
// 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.
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null bencode object.
//
// Bencode cannot represent cyclic data structures and Marshal does not
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null bencode object.
//
// Channel, complex, and function values cannot be encoded in bencode.
// Attempting to encode such a value causes Marshal to return
// an UnsupportedTypeError.
//
// 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(v interface{}) ([]byte, error) {

View File

@ -267,8 +267,8 @@ func stateParseStringLen(s *scanner, c int) int {
if c == ':' {
var err error
if s.strLen, err = strconv.Atoi(string(s.strLenB)); err != nil {
// BUG(emery): don't panic
panic(err)
s.err = err
return scanError
}
//s.strLen+
s.step = stateParseString
@ -295,8 +295,8 @@ func stateParseKeyLen(s *scanner, c int) int {
if c == ':' {
var err error
if s.strLen, err = strconv.Atoi(string(s.strLenB)); err != nil {
// BUG(emery): don't panic
panic(err)
s.err = err
return scanError
}
s.step = stateParseKey
return scanEndKeyLen

View File

@ -17,7 +17,6 @@ import (
"unicode"
)
// A field represents a single field found in a struct.
type field struct {
name string