Fixed skipping, better docs
This commit is contained in:
parent
1dcd7c791f
commit
011343cff3
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
77
decode.go
77
decode.go
|
@ -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:
|
||||
|
|
82
encode.go
82
encode.go
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user