[release/1.3.4] rlp: add "tail" struct tag

This commit is contained in:
Felix Lange
2015-12-21 21:05:20 +01:00
committed by Jeffrey Wilcke
parent 8cb69b9e9b
commit 93077941d3
6 changed files with 163 additions and 26 deletions

View File

@ -63,11 +63,12 @@ type Decoder interface {
// must contain an element for each decoded field. Decode returns an
// error if there are too few or too many elements.
//
// The decoding of struct fields honours one particular struct tag,
// "nil". This tag applies to pointer-typed fields and changes the
// The decoding of struct fields honours two struct tags, "tail" and
// "nil". For an explanation of "tail", see the example.
// The "nil" tag applies to pointer-typed fields and changes the
// decoding rules for the field such that input values of size zero
// decode as a nil pointer. This tag can be useful when decoding recursive
// types.
// decode as a nil pointer. This tag can be useful when decoding
// recursive types.
//
// type StructWithEmptyOK struct {
// Foo *[20]byte `rlp:"nil"`
@ -190,7 +191,7 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
case kind == reflect.String:
return decodeString, nil
case kind == reflect.Slice || kind == reflect.Array:
return makeListDecoder(typ)
return makeListDecoder(typ, tags)
case kind == reflect.Struct:
return makeStructDecoder(typ)
case kind == reflect.Ptr:
@ -264,7 +265,7 @@ func decodeBigInt(s *Stream, val reflect.Value) error {
return nil
}
func makeListDecoder(typ reflect.Type) (decoder, error) {
func makeListDecoder(typ reflect.Type, tag tags) (decoder, error) {
etype := typ.Elem()
if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) {
if typ.Kind() == reflect.Array {
@ -277,15 +278,26 @@ func makeListDecoder(typ reflect.Type) (decoder, error) {
if err != nil {
return nil, err
}
isArray := typ.Kind() == reflect.Array
return func(s *Stream, val reflect.Value) error {
if isArray {
var dec decoder
switch {
case typ.Kind() == reflect.Array:
dec = func(s *Stream, val reflect.Value) error {
return decodeListArray(s, val, etypeinfo.decoder)
} else {
}
case tag.tail:
// A slice with "tail" tag can occur as the last field
// of a struct and is upposed to swallow all remaining
// list elements. The struct decoder already called s.List,
// proceed directly to decoding the elements.
dec = func(s *Stream, val reflect.Value) error {
return decodeSliceElems(s, val, etypeinfo.decoder)
}
default:
dec = func(s *Stream, val reflect.Value) error {
return decodeListSlice(s, val, etypeinfo.decoder)
}
}, nil
}
return dec, nil
}
func decodeListSlice(s *Stream, val reflect.Value, elemdec decoder) error {
@ -297,7 +309,13 @@ func decodeListSlice(s *Stream, val reflect.Value, elemdec decoder) error {
val.Set(reflect.MakeSlice(val.Type(), 0, 0))
return s.ListEnd()
}
if err := decodeSliceElems(s, val, elemdec); err != nil {
return err
}
return s.ListEnd()
}
func decodeSliceElems(s *Stream, val reflect.Value, elemdec decoder) error {
i := 0
for ; ; i++ {
// grow slice if necessary
@ -323,12 +341,11 @@ func decodeListSlice(s *Stream, val reflect.Value, elemdec decoder) error {
if i < val.Len() {
val.SetLen(i)
}
return s.ListEnd()
return nil
}
func decodeListArray(s *Stream, val reflect.Value, elemdec decoder) error {
_, err := s.List()
if err != nil {
if _, err := s.List(); err != nil {
return wrapStreamError(err, val.Type())
}
vlen := val.Len()
@ -398,11 +415,11 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
return nil, err
}
dec := func(s *Stream, val reflect.Value) (err error) {
if _, err = s.List(); err != nil {
if _, err := s.List(); err != nil {
return wrapStreamError(err, typ)
}
for _, f := range fields {
err = f.info.decoder(s, val.Field(f.index))
err := f.info.decoder(s, val.Field(f.index))
if err == EOL {
return &decodeError{msg: "too few elements", typ: typ}
} else if err != nil {