mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-15 17:30:44 +00:00
Merge pull request #542 from gzliudan/rlp
upgarde package rlp to 2024-05-15
This commit is contained in:
commit
09eb6124b2
32 changed files with 4774 additions and 824 deletions
695
rlp/decode.go
695
rlp/decode.go
|
|
@ -26,103 +26,80 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//lint:ignore ST1012 EOL is not an error.
|
||||||
|
|
||||||
|
// EOL is returned when the end of the current list
|
||||||
|
// has been reached during streaming.
|
||||||
|
var EOL = errors.New("rlp: end of list")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
||||||
|
ErrExpectedList = errors.New("rlp: expected List")
|
||||||
|
ErrCanonInt = errors.New("rlp: non-canonical integer format")
|
||||||
|
ErrCanonSize = errors.New("rlp: non-canonical size information")
|
||||||
|
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
|
||||||
|
ErrValueTooLarge = errors.New("rlp: value size exceeds available input length")
|
||||||
|
ErrMoreThanOneValue = errors.New("rlp: input contains more than one value")
|
||||||
|
|
||||||
|
// internal errors
|
||||||
|
errNotInList = errors.New("rlp: call of ListEnd outside of any list")
|
||||||
|
errNotAtEOL = errors.New("rlp: call of ListEnd not positioned at EOL")
|
||||||
|
errUintOverflow = errors.New("rlp: uint overflow")
|
||||||
errNoPointer = errors.New("rlp: interface given to Decode must be a pointer")
|
errNoPointer = errors.New("rlp: interface given to Decode must be a pointer")
|
||||||
errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil")
|
errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil")
|
||||||
|
errUint256Large = errors.New("rlp: value too large for uint256")
|
||||||
|
|
||||||
|
streamPool = sync.Pool{
|
||||||
|
New: func() interface{} { return new(Stream) },
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder is implemented by types that require custom RLP
|
// Decoder is implemented by types that require custom RLP decoding rules or need to decode
|
||||||
// decoding rules or need to decode into private fields.
|
// into private fields.
|
||||||
//
|
//
|
||||||
// The DecodeRLP method should read one value from the given
|
// The DecodeRLP method should read one value from the given Stream. It is not forbidden to
|
||||||
// Stream. It is not forbidden to read less or more, but it might
|
// read less or more, but it might be confusing.
|
||||||
// be confusing.
|
|
||||||
type Decoder interface {
|
type Decoder interface {
|
||||||
DecodeRLP(*Stream) error
|
DecodeRLP(*Stream) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode parses RLP-encoded data from r and stores the result in the
|
// Decode parses RLP-encoded data from r and stores the result in the value pointed to by
|
||||||
// value pointed to by val. Val must be a non-nil pointer. If r does
|
// val. Please see package-level documentation for the decoding rules. Val must be a
|
||||||
// not implement ByteReader, Decode will do its own buffering.
|
// non-nil pointer.
|
||||||
//
|
//
|
||||||
// Decode uses the following type-dependent decoding rules:
|
// If r does not implement ByteReader, Decode will do its own buffering.
|
||||||
//
|
//
|
||||||
// If the type implements the Decoder interface, decode calls
|
// Note that Decode does not set an input limit for all readers and may be vulnerable to
|
||||||
// DecodeRLP.
|
// panics cause by huge value sizes. If you need an input limit, use
|
||||||
//
|
//
|
||||||
// To decode into a pointer, Decode will decode into the value pointed
|
// NewStream(r, limit).Decode(val)
|
||||||
// to. If the pointer is nil, a new value of the pointer's element
|
|
||||||
// type is allocated. If the pointer is non-nil, the existing value
|
|
||||||
// will be reused.
|
|
||||||
//
|
|
||||||
// To decode into a struct, Decode expects the input to be an RLP
|
|
||||||
// list. The decoded elements of the list are assigned to each public
|
|
||||||
// field in the order given by the struct's definition. The input list
|
|
||||||
// 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 certain struct tags, "tail",
|
|
||||||
// "nil" and "-".
|
|
||||||
//
|
|
||||||
// The "-" tag ignores fields.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// type StructWithEmptyOK struct {
|
|
||||||
// Foo *[20]byte `rlp:"nil"`
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// To decode into a slice, the input must be a list and the resulting
|
|
||||||
// slice will contain the input elements in order. For byte slices,
|
|
||||||
// the input must be an RLP string. Array types decode similarly, with
|
|
||||||
// the additional restriction that the number of input elements (or
|
|
||||||
// bytes) must match the array's length.
|
|
||||||
//
|
|
||||||
// To decode into a Go string, the input must be an RLP string. The
|
|
||||||
// input bytes are taken as-is and will not necessarily be valid UTF-8.
|
|
||||||
//
|
|
||||||
// To decode into an unsigned integer type, the input must also be an RLP
|
|
||||||
// string. The bytes are interpreted as a big endian representation of
|
|
||||||
// the integer. If the RLP string is larger than the bit size of the
|
|
||||||
// type, Decode will return an error. Decode also supports *big.Int.
|
|
||||||
// There is no size limit for big integers.
|
|
||||||
//
|
|
||||||
// To decode into an interface value, Decode stores one of these
|
|
||||||
// in the value:
|
|
||||||
//
|
|
||||||
// []interface{}, for RLP lists
|
|
||||||
// []byte, for RLP strings
|
|
||||||
//
|
|
||||||
// Non-empty interface types are not supported, nor are booleans,
|
|
||||||
// signed integers, floating point numbers, maps, channels and
|
|
||||||
// functions.
|
|
||||||
//
|
|
||||||
// Note that Decode does not set an input limit for all readers
|
|
||||||
// and may be vulnerable to panics cause by huge value sizes. If
|
|
||||||
// you need an input limit, use
|
|
||||||
//
|
|
||||||
// NewStream(r, limit).Decode(val)
|
|
||||||
func Decode(r io.Reader, val interface{}) error {
|
func Decode(r io.Reader, val interface{}) error {
|
||||||
// TODO: this could use a Stream from a pool.
|
stream := streamPool.Get().(*Stream)
|
||||||
return NewStream(r, 0).Decode(val)
|
defer streamPool.Put(stream)
|
||||||
|
|
||||||
|
stream.Reset(r, 0)
|
||||||
|
return stream.Decode(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBytes parses RLP data from b into val.
|
// DecodeBytes parses RLP data from b into val. Please see package-level documentation for
|
||||||
// Please see the documentation of Decode for the decoding rules.
|
// the decoding rules. The input must contain exactly one value and no trailing data.
|
||||||
// The input must contain exactly one value and no trailing data.
|
|
||||||
func DecodeBytes(b []byte, val interface{}) error {
|
func DecodeBytes(b []byte, val interface{}) error {
|
||||||
// TODO: this could use a Stream from a pool.
|
r := (*sliceReader)(&b)
|
||||||
r := bytes.NewReader(b)
|
|
||||||
if err := NewStream(r, uint64(len(b))).Decode(val); err != nil {
|
stream := streamPool.Get().(*Stream)
|
||||||
|
defer streamPool.Put(stream)
|
||||||
|
|
||||||
|
stream.Reset(r, uint64(len(b)))
|
||||||
|
if err := stream.Decode(val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if r.Len() > 0 {
|
if len(b) > 0 {
|
||||||
return ErrMoreThanOneValue
|
return ErrMoreThanOneValue
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -173,21 +150,26 @@ func addErrorContext(err error, ctx string) error {
|
||||||
var (
|
var (
|
||||||
decoderInterface = reflect.TypeOf(new(Decoder)).Elem()
|
decoderInterface = reflect.TypeOf(new(Decoder)).Elem()
|
||||||
bigInt = reflect.TypeOf(big.Int{})
|
bigInt = reflect.TypeOf(big.Int{})
|
||||||
|
u256Int = reflect.TypeOf(uint256.Int{})
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
|
func makeDecoder(typ reflect.Type, tags rlpstruct.Tags) (dec decoder, err error) {
|
||||||
kind := typ.Kind()
|
kind := typ.Kind()
|
||||||
switch {
|
switch {
|
||||||
case typ == rawValueType:
|
case typ == rawValueType:
|
||||||
return decodeRawValue, nil
|
return decodeRawValue, nil
|
||||||
case typ.Implements(decoderInterface):
|
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||||
return decodeDecoder, nil
|
|
||||||
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
|
|
||||||
return decodeDecoderNoPtr, nil
|
|
||||||
case typ.AssignableTo(reflect.PtrTo(bigInt)):
|
|
||||||
return decodeBigInt, nil
|
return decodeBigInt, nil
|
||||||
case typ.AssignableTo(bigInt):
|
case typ.AssignableTo(bigInt):
|
||||||
return decodeBigIntNoPtr, nil
|
return decodeBigIntNoPtr, nil
|
||||||
|
case typ == reflect.PointerTo(u256Int):
|
||||||
|
return decodeU256, nil
|
||||||
|
case typ == u256Int:
|
||||||
|
return decodeU256NoPtr, nil
|
||||||
|
case kind == reflect.Ptr:
|
||||||
|
return makePtrDecoder(typ, tags)
|
||||||
|
case reflect.PointerTo(typ).Implements(decoderInterface):
|
||||||
|
return decodeDecoder, nil
|
||||||
case isUint(kind):
|
case isUint(kind):
|
||||||
return decodeUint, nil
|
return decodeUint, nil
|
||||||
case kind == reflect.Bool:
|
case kind == reflect.Bool:
|
||||||
|
|
@ -198,11 +180,6 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
|
||||||
return makeListDecoder(typ, tags)
|
return makeListDecoder(typ, tags)
|
||||||
case kind == reflect.Struct:
|
case kind == reflect.Struct:
|
||||||
return makeStructDecoder(typ)
|
return makeStructDecoder(typ)
|
||||||
case kind == reflect.Ptr:
|
|
||||||
if tags.nilOK {
|
|
||||||
return makeOptionalPtrDecoder(typ)
|
|
||||||
}
|
|
||||||
return makePtrDecoder(typ)
|
|
||||||
case kind == reflect.Interface:
|
case kind == reflect.Interface:
|
||||||
return decodeInterface, nil
|
return decodeInterface, nil
|
||||||
default:
|
default:
|
||||||
|
|
@ -252,35 +229,48 @@ func decodeBigIntNoPtr(s *Stream, val reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBigInt(s *Stream, val reflect.Value) error {
|
func decodeBigInt(s *Stream, val reflect.Value) error {
|
||||||
b, err := s.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return wrapStreamError(err, val.Type())
|
|
||||||
}
|
|
||||||
i := val.Interface().(*big.Int)
|
i := val.Interface().(*big.Int)
|
||||||
if i == nil {
|
if i == nil {
|
||||||
i = new(big.Int)
|
i = new(big.Int)
|
||||||
val.Set(reflect.ValueOf(i))
|
val.Set(reflect.ValueOf(i))
|
||||||
}
|
}
|
||||||
// Reject leading zero bytes
|
|
||||||
if len(b) > 0 && b[0] == 0 {
|
err := s.decodeBigInt(i)
|
||||||
return wrapStreamError(ErrCanonInt, val.Type())
|
if err != nil {
|
||||||
|
return wrapStreamError(err, val.Type())
|
||||||
}
|
}
|
||||||
i.SetBytes(b)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeListDecoder(typ reflect.Type, tag tags) (decoder, error) {
|
func decodeU256NoPtr(s *Stream, val reflect.Value) error {
|
||||||
|
return decodeU256(s, val.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeU256(s *Stream, val reflect.Value) error {
|
||||||
|
i := val.Interface().(*uint256.Int)
|
||||||
|
if i == nil {
|
||||||
|
i = new(uint256.Int)
|
||||||
|
val.Set(reflect.ValueOf(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.ReadUint256(i)
|
||||||
|
if err != nil {
|
||||||
|
return wrapStreamError(err, val.Type())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeListDecoder(typ reflect.Type, tag rlpstruct.Tags) (decoder, error) {
|
||||||
etype := typ.Elem()
|
etype := typ.Elem()
|
||||||
if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) {
|
if etype.Kind() == reflect.Uint8 && !reflect.PointerTo(etype).Implements(decoderInterface) {
|
||||||
if typ.Kind() == reflect.Array {
|
if typ.Kind() == reflect.Array {
|
||||||
return decodeByteArray, nil
|
return decodeByteArray, nil
|
||||||
} else {
|
|
||||||
return decodeByteSlice, nil
|
|
||||||
}
|
}
|
||||||
|
return decodeByteSlice, nil
|
||||||
}
|
}
|
||||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
etypeinfo := theTC.infoWhileGenerating(etype, rlpstruct.Tags{})
|
||||||
if err != nil {
|
if etypeinfo.decoderErr != nil {
|
||||||
return nil, err
|
return nil, etypeinfo.decoderErr
|
||||||
}
|
}
|
||||||
var dec decoder
|
var dec decoder
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -288,7 +278,7 @@ func makeListDecoder(typ reflect.Type, tag tags) (decoder, error) {
|
||||||
dec = func(s *Stream, val reflect.Value) error {
|
dec = func(s *Stream, val reflect.Value) error {
|
||||||
return decodeListArray(s, val, etypeinfo.decoder)
|
return decodeListArray(s, val, etypeinfo.decoder)
|
||||||
}
|
}
|
||||||
case tag.tail:
|
case tag.Tail:
|
||||||
// A slice with "tail" tag can occur as the last field
|
// A slice with "tail" tag can occur as the last field
|
||||||
// of a struct and is supposed to swallow all remaining
|
// of a struct and is supposed to swallow all remaining
|
||||||
// list elements. The struct decoder already called s.List,
|
// list elements. The struct decoder already called s.List,
|
||||||
|
|
@ -381,25 +371,23 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vlen := val.Len()
|
slice := byteArrayBytes(val, val.Len())
|
||||||
switch kind {
|
switch kind {
|
||||||
case Byte:
|
case Byte:
|
||||||
if vlen == 0 {
|
if len(slice) == 0 {
|
||||||
return &decodeError{msg: "input string too long", typ: val.Type()}
|
return &decodeError{msg: "input string too long", typ: val.Type()}
|
||||||
}
|
} else if len(slice) > 1 {
|
||||||
if vlen > 1 {
|
|
||||||
return &decodeError{msg: "input string too short", typ: val.Type()}
|
return &decodeError{msg: "input string too short", typ: val.Type()}
|
||||||
}
|
}
|
||||||
bv, _ := s.Uint()
|
slice[0] = s.byteval
|
||||||
val.Index(0).SetUint(bv)
|
s.kind = -1
|
||||||
case String:
|
case String:
|
||||||
if uint64(vlen) < size {
|
if uint64(len(slice)) < size {
|
||||||
return &decodeError{msg: "input string too long", typ: val.Type()}
|
return &decodeError{msg: "input string too long", typ: val.Type()}
|
||||||
}
|
}
|
||||||
if uint64(vlen) > size {
|
if uint64(len(slice)) > size {
|
||||||
return &decodeError{msg: "input string too short", typ: val.Type()}
|
return &decodeError{msg: "input string too short", typ: val.Type()}
|
||||||
}
|
}
|
||||||
slice := val.Slice(0, vlen).Interface().([]byte)
|
|
||||||
if err := s.readFull(slice); err != nil {
|
if err := s.readFull(slice); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -418,13 +406,25 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.info.decoderErr != nil {
|
||||||
|
return nil, structFieldError{typ, f.index, f.info.decoderErr}
|
||||||
|
}
|
||||||
|
}
|
||||||
dec := func(s *Stream, val reflect.Value) (err error) {
|
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)
|
return wrapStreamError(err, typ)
|
||||||
}
|
}
|
||||||
for _, f := range fields {
|
for i, f := range fields {
|
||||||
err := f.info.decoder(s, val.Field(f.index))
|
err := f.info.decoder(s, val.Field(f.index))
|
||||||
if err == EOL {
|
if err == EOL {
|
||||||
|
if f.optional {
|
||||||
|
// The field is optional, so reaching the end of the list before
|
||||||
|
// reaching the last field is acceptable. All remaining undecoded
|
||||||
|
// fields are zeroed.
|
||||||
|
zeroFields(val, fields[i:])
|
||||||
|
break
|
||||||
|
}
|
||||||
return &decodeError{msg: "too few elements", typ: typ}
|
return &decodeError{msg: "too few elements", typ: typ}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return addErrorContext(err, "."+typ.Field(f.index).Name)
|
return addErrorContext(err, "."+typ.Field(f.index).Name)
|
||||||
|
|
@ -435,15 +435,29 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
|
||||||
return dec, nil
|
return dec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makePtrDecoder creates a decoder that decodes into
|
func zeroFields(structval reflect.Value, fields []field) {
|
||||||
// the pointer's element type.
|
for _, f := range fields {
|
||||||
func makePtrDecoder(typ reflect.Type) (decoder, error) {
|
fv := structval.Field(f.index)
|
||||||
etype := typ.Elem()
|
fv.Set(reflect.Zero(fv.Type()))
|
||||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
dec := func(s *Stream, val reflect.Value) (err error) {
|
}
|
||||||
|
|
||||||
|
// makePtrDecoder creates a decoder that decodes into the pointer's element type.
|
||||||
|
func makePtrDecoder(typ reflect.Type, tag rlpstruct.Tags) (decoder, error) {
|
||||||
|
etype := typ.Elem()
|
||||||
|
etypeinfo := theTC.infoWhileGenerating(etype, rlpstruct.Tags{})
|
||||||
|
switch {
|
||||||
|
case etypeinfo.decoderErr != nil:
|
||||||
|
return nil, etypeinfo.decoderErr
|
||||||
|
case !tag.NilOK:
|
||||||
|
return makeSimplePtrDecoder(etype, etypeinfo), nil
|
||||||
|
default:
|
||||||
|
return makeNilPtrDecoder(etype, etypeinfo, tag), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSimplePtrDecoder(etype reflect.Type, etypeinfo *typeinfo) decoder {
|
||||||
|
return func(s *Stream, val reflect.Value) (err error) {
|
||||||
newval := val
|
newval := val
|
||||||
if val.IsNil() {
|
if val.IsNil() {
|
||||||
newval = reflect.New(etype)
|
newval = reflect.New(etype)
|
||||||
|
|
@ -453,30 +467,39 @@ func makePtrDecoder(typ reflect.Type) (decoder, error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dec, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeOptionalPtrDecoder creates a decoder that decodes empty values
|
// makeNilPtrDecoder creates a decoder that decodes empty values as nil. Non-empty
|
||||||
// as nil. Non-empty values are decoded into a value of the element type,
|
// values are decoded into a value of the element type, just like makePtrDecoder does.
|
||||||
// just like makePtrDecoder does.
|
|
||||||
//
|
//
|
||||||
// This decoder is used for pointer-typed struct fields with struct tag "nil".
|
// This decoder is used for pointer-typed struct fields with struct tag "nil".
|
||||||
func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) {
|
func makeNilPtrDecoder(etype reflect.Type, etypeinfo *typeinfo, ts rlpstruct.Tags) decoder {
|
||||||
etype := typ.Elem()
|
typ := reflect.PointerTo(etype)
|
||||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
nilPtr := reflect.Zero(typ)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
// Determine the value kind that results in nil pointer.
|
||||||
}
|
nilKind := typeNilKind(etype, ts)
|
||||||
dec := func(s *Stream, val reflect.Value) (err error) {
|
|
||||||
|
return func(s *Stream, val reflect.Value) (err error) {
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
if err != nil || size == 0 && kind != Byte {
|
if err != nil {
|
||||||
|
val.Set(nilPtr)
|
||||||
|
return wrapStreamError(err, typ)
|
||||||
|
}
|
||||||
|
// Handle empty values as a nil pointer.
|
||||||
|
if kind != Byte && size == 0 {
|
||||||
|
if kind != nilKind {
|
||||||
|
return &decodeError{
|
||||||
|
msg: fmt.Sprintf("wrong kind of empty value (got %v, want %v)", kind, nilKind),
|
||||||
|
typ: typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
// rearm s.Kind. This is important because the input
|
// rearm s.Kind. This is important because the input
|
||||||
// position must advance to the next value even though
|
// position must advance to the next value even though
|
||||||
// we don't read anything.
|
// we don't read anything.
|
||||||
s.kind = -1
|
s.kind = -1
|
||||||
// set the pointer to nil.
|
val.Set(nilPtr)
|
||||||
val.Set(reflect.Zero(typ))
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
newval := val
|
newval := val
|
||||||
if val.IsNil() {
|
if val.IsNil() {
|
||||||
|
|
@ -487,7 +510,6 @@ func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dec, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ifsliceType = reflect.TypeOf([]interface{}{})
|
var ifsliceType = reflect.TypeOf([]interface{}{})
|
||||||
|
|
@ -516,25 +538,12 @@ func decodeInterface(s *Stream, val reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This decoder is used for non-pointer values of types
|
func decodeDecoder(s *Stream, val reflect.Value) error {
|
||||||
// that implement the Decoder interface using a pointer receiver.
|
|
||||||
func decodeDecoderNoPtr(s *Stream, val reflect.Value) error {
|
|
||||||
return val.Addr().Interface().(Decoder).DecodeRLP(s)
|
return val.Addr().Interface().(Decoder).DecodeRLP(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeDecoder(s *Stream, val reflect.Value) error {
|
|
||||||
// Decoder instances are not handled using the pointer rule if the type
|
|
||||||
// implements Decoder with pointer receiver (i.e. always)
|
|
||||||
// because it might handle empty values specially.
|
|
||||||
// We need to allocate one here in this case, like makePtrDecoder does.
|
|
||||||
if val.Kind() == reflect.Ptr && val.IsNil() {
|
|
||||||
val.Set(reflect.New(val.Type().Elem()))
|
|
||||||
}
|
|
||||||
return val.Interface().(Decoder).DecodeRLP(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kind represents the kind of value contained in an RLP stream.
|
// Kind represents the kind of value contained in an RLP stream.
|
||||||
type Kind int
|
type Kind int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Byte Kind = iota
|
Byte Kind = iota
|
||||||
|
|
@ -555,29 +564,6 @@ func (k Kind) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
// EOL is returned when the end of the current list
|
|
||||||
// has been reached during streaming.
|
|
||||||
EOL = errors.New("rlp: end of list")
|
|
||||||
|
|
||||||
// Actual Errors
|
|
||||||
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
|
||||||
ErrExpectedList = errors.New("rlp: expected List")
|
|
||||||
ErrCanonInt = errors.New("rlp: non-canonical integer format")
|
|
||||||
ErrCanonSize = errors.New("rlp: non-canonical size information")
|
|
||||||
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
|
|
||||||
ErrValueTooLarge = errors.New("rlp: value size exceeds available input length")
|
|
||||||
|
|
||||||
// This error is reported by DecodeBytes if the slice contains
|
|
||||||
// additional data after the first RLP value.
|
|
||||||
ErrMoreThanOneValue = errors.New("rlp: input contains more than one value")
|
|
||||||
|
|
||||||
// internal errors
|
|
||||||
errNotInList = errors.New("rlp: call of ListEnd outside of any list")
|
|
||||||
errNotAtEOL = errors.New("rlp: call of ListEnd not positioned at EOL")
|
|
||||||
errUintOverflow = errors.New("rlp: uint overflow")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ByteReader must be implemented by any input reader for a Stream. It
|
// ByteReader must be implemented by any input reader for a Stream. It
|
||||||
// is implemented by e.g. bufio.Reader and bytes.Reader.
|
// is implemented by e.g. bufio.Reader and bytes.Reader.
|
||||||
type ByteReader interface {
|
type ByteReader interface {
|
||||||
|
|
@ -600,22 +586,16 @@ type ByteReader interface {
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
r ByteReader
|
r ByteReader
|
||||||
|
|
||||||
// number of bytes remaining to be read from r.
|
remaining uint64 // number of bytes remaining to be read from r
|
||||||
remaining uint64
|
size uint64 // size of value ahead
|
||||||
limited bool
|
kinderr error // error from last readKind
|
||||||
|
stack []uint64 // list sizes
|
||||||
// auxiliary buffer for integer decoding
|
uintbuf [32]byte // auxiliary buffer for integer decoding
|
||||||
uintbuf []byte
|
kind Kind // kind of value ahead
|
||||||
|
byteval byte // value of single byte in type tag
|
||||||
kind Kind // kind of value ahead
|
limited bool // true if input limit is in effect
|
||||||
size uint64 // size of value ahead
|
|
||||||
byteval byte // value of single byte in type tag
|
|
||||||
kinderr error // error from last readKind
|
|
||||||
stack []listpos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type listpos struct{ pos, size uint64 }
|
|
||||||
|
|
||||||
// NewStream creates a new decoding stream reading from r.
|
// NewStream creates a new decoding stream reading from r.
|
||||||
//
|
//
|
||||||
// If r implements the ByteReader interface, Stream will
|
// If r implements the ByteReader interface, Stream will
|
||||||
|
|
@ -675,6 +655,37 @@ func (s *Stream) Bytes() ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadBytes decodes the next RLP value and stores the result in b.
|
||||||
|
// The value size must match len(b) exactly.
|
||||||
|
func (s *Stream) ReadBytes(b []byte) error {
|
||||||
|
kind, size, err := s.Kind()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch kind {
|
||||||
|
case Byte:
|
||||||
|
if len(b) != 1 {
|
||||||
|
return fmt.Errorf("input value has wrong size 1, want %d", len(b))
|
||||||
|
}
|
||||||
|
b[0] = s.byteval
|
||||||
|
s.kind = -1 // rearm Kind
|
||||||
|
return nil
|
||||||
|
case String:
|
||||||
|
if uint64(len(b)) != size {
|
||||||
|
return fmt.Errorf("input value has wrong size %d, want %d", size, len(b))
|
||||||
|
}
|
||||||
|
if err = s.readFull(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if size == 1 && b[0] < 128 {
|
||||||
|
return ErrCanonSize
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return ErrExpectedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Raw reads a raw encoded value including RLP type information.
|
// Raw reads a raw encoded value including RLP type information.
|
||||||
func (s *Stream) Raw() ([]byte, error) {
|
func (s *Stream) Raw() ([]byte, error) {
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
|
|
@ -685,8 +696,8 @@ func (s *Stream) Raw() ([]byte, error) {
|
||||||
s.kind = -1 // rearm Kind
|
s.kind = -1 // rearm Kind
|
||||||
return []byte{s.byteval}, nil
|
return []byte{s.byteval}, nil
|
||||||
}
|
}
|
||||||
// the original header has already been read and is no longer
|
// The original header has already been read and is no longer
|
||||||
// available. read content and put a new header in front of it.
|
// available. Read content and put a new header in front of it.
|
||||||
start := headsize(size)
|
start := headsize(size)
|
||||||
buf := make([]byte, uint64(start)+size)
|
buf := make([]byte, uint64(start)+size)
|
||||||
if err := s.readFull(buf[start:]); err != nil {
|
if err := s.readFull(buf[start:]); err != nil {
|
||||||
|
|
@ -703,10 +714,31 @@ func (s *Stream) Raw() ([]byte, error) {
|
||||||
// Uint reads an RLP string of up to 8 bytes and returns its contents
|
// Uint reads an RLP string of up to 8 bytes and returns its contents
|
||||||
// as an unsigned integer. If the input does not contain an RLP string, the
|
// as an unsigned integer. If the input does not contain an RLP string, the
|
||||||
// returned error will be ErrExpectedString.
|
// returned error will be ErrExpectedString.
|
||||||
|
//
|
||||||
|
// Deprecated: use s.Uint64 instead.
|
||||||
func (s *Stream) Uint() (uint64, error) {
|
func (s *Stream) Uint() (uint64, error) {
|
||||||
return s.uint(64)
|
return s.uint(64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Uint64() (uint64, error) {
|
||||||
|
return s.uint(64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Uint32() (uint32, error) {
|
||||||
|
i, err := s.uint(32)
|
||||||
|
return uint32(i), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Uint16() (uint16, error) {
|
||||||
|
i, err := s.uint(16)
|
||||||
|
return uint16(i), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) Uint8() (uint8, error) {
|
||||||
|
i, err := s.uint(8)
|
||||||
|
return uint8(i), err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Stream) uint(maxbits int) (uint64, error) {
|
func (s *Stream) uint(maxbits int) (uint64, error) {
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -769,7 +801,14 @@ func (s *Stream) List() (size uint64, err error) {
|
||||||
if kind != List {
|
if kind != List {
|
||||||
return 0, ErrExpectedList
|
return 0, ErrExpectedList
|
||||||
}
|
}
|
||||||
s.stack = append(s.stack, listpos{0, size})
|
|
||||||
|
// Remove size of inner list from outer list before pushing the new size
|
||||||
|
// onto the stack. This ensures that the remaining outer list size will
|
||||||
|
// be correct after the matching call to ListEnd.
|
||||||
|
if inList, limit := s.listLimit(); inList {
|
||||||
|
s.stack[len(s.stack)-1] = limit - size
|
||||||
|
}
|
||||||
|
s.stack = append(s.stack, size)
|
||||||
s.kind = -1
|
s.kind = -1
|
||||||
s.size = 0
|
s.size = 0
|
||||||
return size, nil
|
return size, nil
|
||||||
|
|
@ -778,22 +817,116 @@ func (s *Stream) List() (size uint64, err error) {
|
||||||
// ListEnd returns to the enclosing list.
|
// ListEnd returns to the enclosing list.
|
||||||
// The input reader must be positioned at the end of a list.
|
// The input reader must be positioned at the end of a list.
|
||||||
func (s *Stream) ListEnd() error {
|
func (s *Stream) ListEnd() error {
|
||||||
if len(s.stack) == 0 {
|
// Ensure that no more data is remaining in the current list.
|
||||||
|
if inList, listLimit := s.listLimit(); !inList {
|
||||||
return errNotInList
|
return errNotInList
|
||||||
}
|
} else if listLimit > 0 {
|
||||||
tos := s.stack[len(s.stack)-1]
|
|
||||||
if tos.pos != tos.size {
|
|
||||||
return errNotAtEOL
|
return errNotAtEOL
|
||||||
}
|
}
|
||||||
s.stack = s.stack[:len(s.stack)-1] // pop
|
s.stack = s.stack[:len(s.stack)-1] // pop
|
||||||
if len(s.stack) > 0 {
|
|
||||||
s.stack[len(s.stack)-1].pos += tos.size
|
|
||||||
}
|
|
||||||
s.kind = -1
|
s.kind = -1
|
||||||
s.size = 0
|
s.size = 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoreDataInList reports whether the current list context contains
|
||||||
|
// more data to be read.
|
||||||
|
func (s *Stream) MoreDataInList() bool {
|
||||||
|
_, listLimit := s.listLimit()
|
||||||
|
return listLimit > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigInt decodes an arbitrary-size integer value.
|
||||||
|
func (s *Stream) BigInt() (*big.Int, error) {
|
||||||
|
i := new(big.Int)
|
||||||
|
if err := s.decodeBigInt(i); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) decodeBigInt(dst *big.Int) error {
|
||||||
|
var buffer []byte
|
||||||
|
kind, size, err := s.Kind()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case kind == List:
|
||||||
|
return ErrExpectedString
|
||||||
|
case kind == Byte:
|
||||||
|
buffer = s.uintbuf[:1]
|
||||||
|
buffer[0] = s.byteval
|
||||||
|
s.kind = -1 // re-arm Kind
|
||||||
|
case size == 0:
|
||||||
|
// Avoid zero-length read.
|
||||||
|
s.kind = -1
|
||||||
|
case size <= uint64(len(s.uintbuf)):
|
||||||
|
// For integers smaller than s.uintbuf, allocating a buffer
|
||||||
|
// can be avoided.
|
||||||
|
buffer = s.uintbuf[:size]
|
||||||
|
if err := s.readFull(buffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Reject inputs where single byte encoding should have been used.
|
||||||
|
if size == 1 && buffer[0] < 128 {
|
||||||
|
return ErrCanonSize
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// For large integers, a temporary buffer is needed.
|
||||||
|
buffer = make([]byte, size)
|
||||||
|
if err := s.readFull(buffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject leading zero bytes.
|
||||||
|
if len(buffer) > 0 && buffer[0] == 0 {
|
||||||
|
return ErrCanonInt
|
||||||
|
}
|
||||||
|
// Set the integer bytes.
|
||||||
|
dst.SetBytes(buffer)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUint256 decodes the next value as a uint256.
|
||||||
|
func (s *Stream) ReadUint256(dst *uint256.Int) error {
|
||||||
|
var buffer []byte
|
||||||
|
kind, size, err := s.Kind()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case kind == List:
|
||||||
|
return ErrExpectedString
|
||||||
|
case kind == Byte:
|
||||||
|
buffer = s.uintbuf[:1]
|
||||||
|
buffer[0] = s.byteval
|
||||||
|
s.kind = -1 // re-arm Kind
|
||||||
|
case size == 0:
|
||||||
|
// Avoid zero-length read.
|
||||||
|
s.kind = -1
|
||||||
|
case size <= uint64(len(s.uintbuf)):
|
||||||
|
// All possible uint256 values fit into s.uintbuf.
|
||||||
|
buffer = s.uintbuf[:size]
|
||||||
|
if err := s.readFull(buffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Reject inputs where single byte encoding should have been used.
|
||||||
|
if size == 1 && buffer[0] < 128 {
|
||||||
|
return ErrCanonSize
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errUint256Large
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject leading zero bytes.
|
||||||
|
if len(buffer) > 0 && buffer[0] == 0 {
|
||||||
|
return ErrCanonInt
|
||||||
|
}
|
||||||
|
// Set the integer bytes.
|
||||||
|
dst.SetBytes(buffer)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Decode decodes a value and stores the result in the value pointed
|
// Decode decodes a value and stores the result in the value pointed
|
||||||
// to by val. Please see the documentation for the Decode function
|
// to by val. Please see the documentation for the Decode function
|
||||||
// to learn about the decoding rules.
|
// to learn about the decoding rules.
|
||||||
|
|
@ -809,14 +942,14 @@ func (s *Stream) Decode(val interface{}) error {
|
||||||
if rval.IsNil() {
|
if rval.IsNil() {
|
||||||
return errDecodeIntoNil
|
return errDecodeIntoNil
|
||||||
}
|
}
|
||||||
info, err := cachedTypeInfo(rtyp.Elem(), tags{})
|
decoder, err := cachedDecoder(rtyp.Elem())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = info.decoder(s, rval.Elem())
|
err = decoder(s, rval.Elem())
|
||||||
if decErr, ok := err.(*decodeError); ok && len(decErr.ctx) > 0 {
|
if decErr, ok := err.(*decodeError); ok && len(decErr.ctx) > 0 {
|
||||||
// add decode target type to error so context has more meaning
|
// Add decode target type to error so context has more meaning.
|
||||||
decErr.ctx = append(decErr.ctx, fmt.Sprint("(", rtyp.Elem(), ")"))
|
decErr.ctx = append(decErr.ctx, fmt.Sprint("(", rtyp.Elem(), ")"))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
@ -839,6 +972,9 @@ func (s *Stream) Reset(r io.Reader, inputLimit uint64) {
|
||||||
case *bytes.Reader:
|
case *bytes.Reader:
|
||||||
s.remaining = uint64(br.Len())
|
s.remaining = uint64(br.Len())
|
||||||
s.limited = true
|
s.limited = true
|
||||||
|
case *bytes.Buffer:
|
||||||
|
s.remaining = uint64(br.Len())
|
||||||
|
s.limited = true
|
||||||
case *strings.Reader:
|
case *strings.Reader:
|
||||||
s.remaining = uint64(br.Len())
|
s.remaining = uint64(br.Len())
|
||||||
s.limited = true
|
s.limited = true
|
||||||
|
|
@ -857,9 +993,8 @@ func (s *Stream) Reset(r io.Reader, inputLimit uint64) {
|
||||||
s.size = 0
|
s.size = 0
|
||||||
s.kind = -1
|
s.kind = -1
|
||||||
s.kinderr = nil
|
s.kinderr = nil
|
||||||
if s.uintbuf == nil {
|
s.byteval = 0
|
||||||
s.uintbuf = make([]byte, 8)
|
s.uintbuf = [32]byte{}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kind returns the kind and size of the next value in the
|
// Kind returns the kind and size of the next value in the
|
||||||
|
|
@ -874,35 +1009,29 @@ func (s *Stream) Reset(r io.Reader, inputLimit uint64) {
|
||||||
// the value. Subsequent calls to Kind (until the value is decoded)
|
// the value. Subsequent calls to Kind (until the value is decoded)
|
||||||
// will not advance the input reader and return cached information.
|
// will not advance the input reader and return cached information.
|
||||||
func (s *Stream) Kind() (kind Kind, size uint64, err error) {
|
func (s *Stream) Kind() (kind Kind, size uint64, err error) {
|
||||||
var tos *listpos
|
if s.kind >= 0 {
|
||||||
if len(s.stack) > 0 {
|
return s.kind, s.size, s.kinderr
|
||||||
tos = &s.stack[len(s.stack)-1]
|
|
||||||
}
|
}
|
||||||
if s.kind < 0 {
|
|
||||||
s.kinderr = nil
|
// Check for end of list. This needs to be done here because readKind
|
||||||
// Don't read further if we're at the end of the
|
// checks against the list size, and would return the wrong error.
|
||||||
// innermost list.
|
inList, listLimit := s.listLimit()
|
||||||
if tos != nil && tos.pos == tos.size {
|
if inList && listLimit == 0 {
|
||||||
return 0, 0, EOL
|
return 0, 0, EOL
|
||||||
}
|
}
|
||||||
s.kind, s.size, s.kinderr = s.readKind()
|
// Read the actual size tag.
|
||||||
if s.kinderr == nil {
|
s.kind, s.size, s.kinderr = s.readKind()
|
||||||
if tos == nil {
|
if s.kinderr == nil {
|
||||||
// At toplevel, check that the value is smaller
|
// Check the data size of the value ahead against input limits. This
|
||||||
// than the remaining input length.
|
// is done here because many decoders require allocating an input
|
||||||
if s.limited && s.size > s.remaining {
|
// buffer matching the value size. Checking it here protects those
|
||||||
s.kinderr = ErrValueTooLarge
|
// decoders from inputs declaring very large value size.
|
||||||
}
|
if inList && s.size > listLimit {
|
||||||
} else {
|
s.kinderr = ErrElemTooLarge
|
||||||
// Inside a list, check that the value doesn't overflow the list.
|
} else if s.limited && s.size > s.remaining {
|
||||||
if s.size > tos.size-tos.pos {
|
s.kinderr = ErrValueTooLarge
|
||||||
s.kinderr = ErrElemTooLarge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: this might return a sticky error generated
|
|
||||||
// by an earlier call to readKind.
|
|
||||||
return s.kind, s.size, s.kinderr
|
return s.kind, s.size, s.kinderr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -929,37 +1058,35 @@ func (s *Stream) readKind() (kind Kind, size uint64, err error) {
|
||||||
s.byteval = b
|
s.byteval = b
|
||||||
return Byte, 0, nil
|
return Byte, 0, nil
|
||||||
case b < 0xB8:
|
case b < 0xB8:
|
||||||
// Otherwise, if a string is 0-55 bytes long,
|
// Otherwise, if a string is 0-55 bytes long, the RLP encoding consists
|
||||||
// the RLP encoding consists of a single byte with value 0x80 plus the
|
// of a single byte with value 0x80 plus the length of the string
|
||||||
// length of the string followed by the string. The range of the first
|
// followed by the string. The range of the first byte is thus [0x80, 0xB7].
|
||||||
// byte is thus [0x80, 0xB7].
|
|
||||||
return String, uint64(b - 0x80), nil
|
return String, uint64(b - 0x80), nil
|
||||||
case b < 0xC0:
|
case b < 0xC0:
|
||||||
// If a string is more than 55 bytes long, the
|
// If a string is more than 55 bytes long, the RLP encoding consists of a
|
||||||
// RLP encoding consists of a single byte with value 0xB7 plus the length
|
// single byte with value 0xB7 plus the length of the length of the
|
||||||
// of the length of the string in binary form, followed by the length of
|
// string in binary form, followed by the length of the string, followed
|
||||||
// the string, followed by the string. For example, a length-1024 string
|
// by the string. For example, a length-1024 string would be encoded as
|
||||||
// would be encoded as 0xB90400 followed by the string. The range of
|
// 0xB90400 followed by the string. The range of the first byte is thus
|
||||||
// the first byte is thus [0xB8, 0xBF].
|
// [0xB8, 0xBF].
|
||||||
size, err = s.readUint(b - 0xB7)
|
size, err = s.readUint(b - 0xB7)
|
||||||
if err == nil && size < 56 {
|
if err == nil && size < 56 {
|
||||||
err = ErrCanonSize
|
err = ErrCanonSize
|
||||||
}
|
}
|
||||||
return String, size, err
|
return String, size, err
|
||||||
case b < 0xF8:
|
case b < 0xF8:
|
||||||
// If the total payload of a list
|
// If the total payload of a list (i.e. the combined length of all its
|
||||||
// (i.e. the combined length of all its items) is 0-55 bytes long, the
|
// items) is 0-55 bytes long, the RLP encoding consists of a single byte
|
||||||
// RLP encoding consists of a single byte with value 0xC0 plus the length
|
// with value 0xC0 plus the length of the list followed by the
|
||||||
// of the list followed by the concatenation of the RLP encodings of the
|
// concatenation of the RLP encodings of the items. The range of the
|
||||||
// items. The range of the first byte is thus [0xC0, 0xF7].
|
// first byte is thus [0xC0, 0xF7].
|
||||||
return List, uint64(b - 0xC0), nil
|
return List, uint64(b - 0xC0), nil
|
||||||
default:
|
default:
|
||||||
// If the total payload of a list is more than 55 bytes long,
|
// If the total payload of a list is more than 55 bytes long, the RLP
|
||||||
// the RLP encoding consists of a single byte with value 0xF7
|
// encoding consists of a single byte with value 0xF7 plus the length of
|
||||||
// plus the length of the length of the payload in binary
|
// the length of the payload in binary form, followed by the length of
|
||||||
// form, followed by the length of the payload, followed by
|
// the payload, followed by the concatenation of the RLP encodings of
|
||||||
// the concatenation of the RLP encodings of the items. The
|
// the items. The range of the first byte is thus [0xF8, 0xFF].
|
||||||
// range of the first byte is thus [0xF8, 0xFF].
|
|
||||||
size, err = s.readUint(b - 0xF7)
|
size, err = s.readUint(b - 0xF7)
|
||||||
if err == nil && size < 56 {
|
if err == nil && size < 56 {
|
||||||
err = ErrCanonSize
|
err = ErrCanonSize
|
||||||
|
|
@ -977,23 +1104,22 @@ func (s *Stream) readUint(size byte) (uint64, error) {
|
||||||
b, err := s.readByte()
|
b, err := s.readByte()
|
||||||
return uint64(b), err
|
return uint64(b), err
|
||||||
default:
|
default:
|
||||||
|
buffer := s.uintbuf[:8]
|
||||||
|
clear(buffer)
|
||||||
start := int(8 - size)
|
start := int(8 - size)
|
||||||
for i := 0; i < start; i++ {
|
if err := s.readFull(buffer[start:]); err != nil {
|
||||||
s.uintbuf[i] = 0
|
|
||||||
}
|
|
||||||
if err := s.readFull(s.uintbuf[start:]); err != nil {
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if s.uintbuf[start] == 0 {
|
if buffer[start] == 0 {
|
||||||
// Note: readUint is also used to decode integer
|
// Note: readUint is also used to decode integer values.
|
||||||
// values. The error needs to be adjusted to become
|
// The error needs to be adjusted to become ErrCanonInt in this case.
|
||||||
// ErrCanonInt in this case.
|
|
||||||
return 0, ErrCanonSize
|
return 0, ErrCanonSize
|
||||||
}
|
}
|
||||||
return binary.BigEndian.Uint64(s.uintbuf), nil
|
return binary.BigEndian.Uint64(buffer[:]), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readFull reads into buf from the underlying stream.
|
||||||
func (s *Stream) readFull(buf []byte) (err error) {
|
func (s *Stream) readFull(buf []byte) (err error) {
|
||||||
if err := s.willRead(uint64(len(buf))); err != nil {
|
if err := s.willRead(uint64(len(buf))); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -1004,11 +1130,18 @@ func (s *Stream) readFull(buf []byte) (err error) {
|
||||||
n += nn
|
n += nn
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = io.ErrUnexpectedEOF
|
if n < len(buf) {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else {
|
||||||
|
// Readers are allowed to give EOF even though the read succeeded.
|
||||||
|
// In such cases, we discard the EOF, like io.ReadFull() does.
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readByte reads a single byte from the underlying stream.
|
||||||
func (s *Stream) readByte() (byte, error) {
|
func (s *Stream) readByte() (byte, error) {
|
||||||
if err := s.willRead(1); err != nil {
|
if err := s.willRead(1); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1020,16 +1153,16 @@ func (s *Stream) readByte() (byte, error) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// willRead is called before any read from the underlying stream. It checks
|
||||||
|
// n against size limits, and updates the limits if n doesn't overflow them.
|
||||||
func (s *Stream) willRead(n uint64) error {
|
func (s *Stream) willRead(n uint64) error {
|
||||||
s.kind = -1 // rearm Kind
|
s.kind = -1 // rearm Kind
|
||||||
|
|
||||||
if len(s.stack) > 0 {
|
if inList, limit := s.listLimit(); inList {
|
||||||
// check list overflow
|
if n > limit {
|
||||||
tos := s.stack[len(s.stack)-1]
|
|
||||||
if n > tos.size-tos.pos {
|
|
||||||
return ErrElemTooLarge
|
return ErrElemTooLarge
|
||||||
}
|
}
|
||||||
s.stack[len(s.stack)-1].pos += n
|
s.stack[len(s.stack)-1] = limit - n
|
||||||
}
|
}
|
||||||
if s.limited {
|
if s.limited {
|
||||||
if n > s.remaining {
|
if n > s.remaining {
|
||||||
|
|
@ -1039,3 +1172,31 @@ func (s *Stream) willRead(n uint64) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listLimit returns the amount of data remaining in the innermost list.
|
||||||
|
func (s *Stream) listLimit() (inList bool, limit uint64) {
|
||||||
|
if len(s.stack) == 0 {
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
return true, s.stack[len(s.stack)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceReader []byte
|
||||||
|
|
||||||
|
func (sr *sliceReader) Read(b []byte) (int, error) {
|
||||||
|
if len(*sr) == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n := copy(b, *sr)
|
||||||
|
*sr = (*sr)[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr *sliceReader) ReadByte() (byte, error) {
|
||||||
|
if len(*sr) == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
b := (*sr)[0]
|
||||||
|
*sr = (*sr)[1:]
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStreamKind(t *testing.T) {
|
func TestStreamKind(t *testing.T) {
|
||||||
|
|
@ -284,6 +287,47 @@ func TestStreamRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStreamReadBytes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
size int
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
// kind List
|
||||||
|
{input: "C0", size: 1, err: "rlp: expected String or Byte"},
|
||||||
|
// kind Byte
|
||||||
|
{input: "04", size: 0, err: "input value has wrong size 1, want 0"},
|
||||||
|
{input: "04", size: 1},
|
||||||
|
{input: "04", size: 2, err: "input value has wrong size 1, want 2"},
|
||||||
|
// kind String
|
||||||
|
{input: "820102", size: 0, err: "input value has wrong size 2, want 0"},
|
||||||
|
{input: "820102", size: 1, err: "input value has wrong size 2, want 1"},
|
||||||
|
{input: "820102", size: 2},
|
||||||
|
{input: "820102", size: 3, err: "input value has wrong size 2, want 3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
name := fmt.Sprintf("input_%s/size_%d", test.input, test.size)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
s := NewStream(bytes.NewReader(unhex(test.input)), 0)
|
||||||
|
b := make([]byte, test.size)
|
||||||
|
err := s.ReadBytes(b)
|
||||||
|
if test.err == "" {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %q", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, got nil")
|
||||||
|
} else if err.Error() != test.err {
|
||||||
|
t.Errorf("wrong error %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeErrors(t *testing.T) {
|
func TestDecodeErrors(t *testing.T) {
|
||||||
r := bytes.NewReader(nil)
|
r := bytes.NewReader(nil)
|
||||||
|
|
||||||
|
|
@ -327,6 +371,15 @@ type recstruct struct {
|
||||||
Child *recstruct `rlp:"nil"`
|
Child *recstruct `rlp:"nil"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bigIntStruct struct {
|
||||||
|
I *big.Int
|
||||||
|
B string
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidNilTag struct {
|
||||||
|
X []byte `rlp:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
type invalidTail1 struct {
|
type invalidTail1 struct {
|
||||||
A uint `rlp:"tail"`
|
A uint `rlp:"tail"`
|
||||||
B string
|
B string
|
||||||
|
|
@ -347,19 +400,79 @@ type tailUint struct {
|
||||||
Tail []uint `rlp:"tail"`
|
Tail []uint `rlp:"tail"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type tailPrivateFields struct {
|
||||||
veryBigInt = big.NewInt(0).Add(
|
A uint
|
||||||
big.NewInt(0).Lsh(big.NewInt(0xFFFFFFFFFFFFFF), 16),
|
Tail []uint `rlp:"tail"`
|
||||||
big.NewInt(0xFFFF),
|
x, y bool //lint:ignore U1000 unused fields required for testing purposes.
|
||||||
)
|
}
|
||||||
)
|
|
||||||
|
|
||||||
type hasIgnoredField struct {
|
type nilListUint struct {
|
||||||
|
X *uint `rlp:"nilList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type nilStringSlice struct {
|
||||||
|
X *[]uint `rlp:"nilString"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type intField struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalFields struct {
|
||||||
|
A uint
|
||||||
|
B uint `rlp:"optional"`
|
||||||
|
C uint `rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalAndTailField struct {
|
||||||
|
A uint
|
||||||
|
B uint `rlp:"optional"`
|
||||||
|
Tail []uint `rlp:"tail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalBigIntField struct {
|
||||||
|
A uint
|
||||||
|
B *big.Int `rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalPtrField struct {
|
||||||
|
A uint
|
||||||
|
B *[3]byte `rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type nonOptionalPtrField struct {
|
||||||
|
A uint
|
||||||
|
B *[3]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type multipleOptionalFields struct {
|
||||||
|
A *[3]byte `rlp:"optional"`
|
||||||
|
B *[3]byte `rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionalPtrFieldNil struct {
|
||||||
|
A uint
|
||||||
|
B *[3]byte `rlp:"optional,nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ignoredField struct {
|
||||||
A uint
|
A uint
|
||||||
B uint `rlp:"-"`
|
B uint `rlp:"-"`
|
||||||
C uint
|
C uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
veryBigInt = new(big.Int).Add(
|
||||||
|
new(big.Int).Lsh(big.NewInt(0xFFFFFFFFFFFFFF), 16),
|
||||||
|
big.NewInt(0xFFFF),
|
||||||
|
)
|
||||||
|
veryVeryBigInt = new(big.Int).Exp(veryBigInt, big.NewInt(8), nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
veryBigInt256, _ = uint256.FromBig(veryBigInt)
|
||||||
|
)
|
||||||
|
|
||||||
var decodeTests = []decodeTest{
|
var decodeTests = []decodeTest{
|
||||||
// booleans
|
// booleans
|
||||||
{input: "01", ptr: new(bool), value: true},
|
{input: "01", ptr: new(bool), value: true},
|
||||||
|
|
@ -428,12 +541,31 @@ var decodeTests = []decodeTest{
|
||||||
{input: "C0", ptr: new(string), error: "rlp: expected input string or byte for string"},
|
{input: "C0", ptr: new(string), error: "rlp: expected input string or byte for string"},
|
||||||
|
|
||||||
// big ints
|
// big ints
|
||||||
|
{input: "80", ptr: new(*big.Int), value: big.NewInt(0)},
|
||||||
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
|
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
|
||||||
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt},
|
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt},
|
||||||
|
{input: "B848FFFFFFFFFFFFFFFFF800000000000000001BFFFFFFFFFFFFFFFFC8000000000000000045FFFFFFFFFFFFFFFFC800000000000000001BFFFFFFFFFFFFFFFFF8000000000000000001", ptr: new(*big.Int), value: veryVeryBigInt},
|
||||||
{input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works
|
{input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works
|
||||||
|
|
||||||
|
// big int errors
|
||||||
{input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"},
|
{input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"},
|
||||||
{input: "820001", ptr: new(big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"},
|
{input: "00", ptr: new(*big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"},
|
||||||
{input: "8105", ptr: new(big.Int), error: "rlp: non-canonical size information for *big.Int"},
|
{input: "820001", ptr: new(*big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"},
|
||||||
|
{input: "8105", ptr: new(*big.Int), error: "rlp: non-canonical size information for *big.Int"},
|
||||||
|
|
||||||
|
// uint256
|
||||||
|
{input: "80", ptr: new(*uint256.Int), value: uint256.NewInt(0)},
|
||||||
|
{input: "01", ptr: new(*uint256.Int), value: uint256.NewInt(1)},
|
||||||
|
{input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(math.MaxUint64)},
|
||||||
|
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: veryBigInt256},
|
||||||
|
{input: "10", ptr: new(uint256.Int), value: *uint256.NewInt(16)}, // non-pointer also works
|
||||||
|
|
||||||
|
// uint256 errors
|
||||||
|
{input: "C0", ptr: new(*uint256.Int), error: "rlp: expected input string or byte for *uint256.Int"},
|
||||||
|
{input: "00", ptr: new(*uint256.Int), error: "rlp: non-canonical integer (leading zero bytes) for *uint256.Int"},
|
||||||
|
{input: "820001", ptr: new(*uint256.Int), error: "rlp: non-canonical integer (leading zero bytes) for *uint256.Int"},
|
||||||
|
{input: "8105", ptr: new(*uint256.Int), error: "rlp: non-canonical size information for *uint256.Int"},
|
||||||
|
{input: "A1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00", ptr: new(*uint256.Int), error: "rlp: value too large for uint256"},
|
||||||
|
|
||||||
// structs
|
// structs
|
||||||
{
|
{
|
||||||
|
|
@ -446,6 +578,13 @@ var decodeTests = []decodeTest{
|
||||||
ptr: new(recstruct),
|
ptr: new(recstruct),
|
||||||
value: recstruct{1, &recstruct{2, &recstruct{3, nil}}},
|
value: recstruct{1, &recstruct{2, &recstruct{3, nil}}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// This checks that empty big.Int works correctly in struct context. It's easy to
|
||||||
|
// miss the update of s.kind for this case, so it needs its own test.
|
||||||
|
input: "C58083343434",
|
||||||
|
ptr: new(bigIntStruct),
|
||||||
|
value: bigIntStruct{new(big.Int), "444"},
|
||||||
|
},
|
||||||
|
|
||||||
// struct errors
|
// struct errors
|
||||||
{
|
{
|
||||||
|
|
@ -479,20 +618,20 @@ var decodeTests = []decodeTest{
|
||||||
error: "rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I",
|
error: "rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "C0",
|
input: "C103",
|
||||||
ptr: new(invalidTail1),
|
ptr: new(intField),
|
||||||
error: "rlp: invalid struct tag \"tail\" for rlp.invalidTail1.A (must be on last field)",
|
error: "rlp: type int is not RLP-serializable (struct field rlp.intField.X)",
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "C0",
|
|
||||||
ptr: new(invalidTail2),
|
|
||||||
error: "rlp: invalid struct tag \"tail\" for rlp.invalidTail2.B (field type is not slice)",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "C50102C20102",
|
input: "C50102C20102",
|
||||||
ptr: new(tailUint),
|
ptr: new(tailUint),
|
||||||
error: "rlp: expected input string or byte for uint, decoding into (rlp.tailUint).Tail[1]",
|
error: "rlp: expected input string or byte for uint, decoding into (rlp.tailUint).Tail[1]",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "C0",
|
||||||
|
ptr: new(invalidNilTag),
|
||||||
|
error: `rlp: invalid struct tag "nil" for rlp.invalidNilTag.X (field is not a pointer)`,
|
||||||
|
},
|
||||||
|
|
||||||
// struct tag "tail"
|
// struct tag "tail"
|
||||||
{
|
{
|
||||||
|
|
@ -510,12 +649,192 @@ var decodeTests = []decodeTest{
|
||||||
ptr: new(tailRaw),
|
ptr: new(tailRaw),
|
||||||
value: tailRaw{A: 1, Tail: []RawValue{}},
|
value: tailRaw{A: 1, Tail: []RawValue{}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "C3010203",
|
||||||
|
ptr: new(tailPrivateFields),
|
||||||
|
value: tailPrivateFields{A: 1, Tail: []uint{2, 3}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C0",
|
||||||
|
ptr: new(invalidTail1),
|
||||||
|
error: `rlp: invalid struct tag "tail" for rlp.invalidTail1.A (must be on last field)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C0",
|
||||||
|
ptr: new(invalidTail2),
|
||||||
|
error: `rlp: invalid struct tag "tail" for rlp.invalidTail2.B (field type is not slice)`,
|
||||||
|
},
|
||||||
|
|
||||||
// struct tag "-"
|
// struct tag "-"
|
||||||
{
|
{
|
||||||
input: "C20102",
|
input: "C20102",
|
||||||
ptr: new(hasIgnoredField),
|
ptr: new(ignoredField),
|
||||||
value: hasIgnoredField{A: 1, C: 2},
|
value: ignoredField{A: 1, C: 2},
|
||||||
|
},
|
||||||
|
|
||||||
|
// struct tag "nilList"
|
||||||
|
{
|
||||||
|
input: "C180",
|
||||||
|
ptr: new(nilListUint),
|
||||||
|
error: "rlp: wrong kind of empty value (got String, want List) for *uint, decoding into (rlp.nilListUint).X",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C1C0",
|
||||||
|
ptr: new(nilListUint),
|
||||||
|
value: nilListUint{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C103",
|
||||||
|
ptr: new(nilListUint),
|
||||||
|
value: func() interface{} {
|
||||||
|
v := uint(3)
|
||||||
|
return nilListUint{X: &v}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
|
||||||
|
// struct tag "nilString"
|
||||||
|
{
|
||||||
|
input: "C1C0",
|
||||||
|
ptr: new(nilStringSlice),
|
||||||
|
error: "rlp: wrong kind of empty value (got List, want String) for *[]uint, decoding into (rlp.nilStringSlice).X",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C180",
|
||||||
|
ptr: new(nilStringSlice),
|
||||||
|
value: nilStringSlice{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C2C103",
|
||||||
|
ptr: new(nilStringSlice),
|
||||||
|
value: nilStringSlice{X: &[]uint{3}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// struct tag "optional"
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: new(optionalFields),
|
||||||
|
value: optionalFields{1, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: new(optionalFields),
|
||||||
|
value: optionalFields{1, 2, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C3010203",
|
||||||
|
ptr: new(optionalFields),
|
||||||
|
value: optionalFields{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C401020304",
|
||||||
|
ptr: new(optionalFields),
|
||||||
|
error: "rlp: input list has too many elements for rlp.optionalFields",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: new(optionalAndTailField),
|
||||||
|
value: optionalAndTailField{A: 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: new(optionalAndTailField),
|
||||||
|
value: optionalAndTailField{A: 1, B: 2, Tail: []uint{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C401020304",
|
||||||
|
ptr: new(optionalAndTailField),
|
||||||
|
value: optionalAndTailField{A: 1, B: 2, Tail: []uint{3, 4}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: new(optionalBigIntField),
|
||||||
|
value: optionalBigIntField{A: 1, B: nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: new(optionalBigIntField),
|
||||||
|
value: optionalBigIntField{A: 1, B: big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: new(optionalPtrField),
|
||||||
|
value: optionalPtrField{A: 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20180", // not accepted because "optional" doesn't enable "nil"
|
||||||
|
ptr: new(optionalPtrField),
|
||||||
|
error: "rlp: input string too short for [3]uint8, decoding into (rlp.optionalPtrField).B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: new(optionalPtrField),
|
||||||
|
error: "rlp: input string too short for [3]uint8, decoding into (rlp.optionalPtrField).B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C50183010203",
|
||||||
|
ptr: new(optionalPtrField),
|
||||||
|
value: optionalPtrField{A: 1, B: &[3]byte{1, 2, 3}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// all optional fields nil
|
||||||
|
input: "C0",
|
||||||
|
ptr: new(multipleOptionalFields),
|
||||||
|
value: multipleOptionalFields{A: nil, B: nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// all optional fields set
|
||||||
|
input: "C88301020383010203",
|
||||||
|
ptr: new(multipleOptionalFields),
|
||||||
|
value: multipleOptionalFields{A: &[3]byte{1, 2, 3}, B: &[3]byte{1, 2, 3}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// nil optional field appears before a non-nil one
|
||||||
|
input: "C58083010203",
|
||||||
|
ptr: new(multipleOptionalFields),
|
||||||
|
error: "rlp: input string too short for [3]uint8, decoding into (rlp.multipleOptionalFields).A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// decode a nil ptr into a ptr that is not nil or not optional
|
||||||
|
input: "C20180",
|
||||||
|
ptr: new(nonOptionalPtrField),
|
||||||
|
error: "rlp: input string too short for [3]uint8, decoding into (rlp.nonOptionalPtrField).B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: new(optionalPtrFieldNil),
|
||||||
|
value: optionalPtrFieldNil{A: 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20180", // accepted because "nil" tag allows empty input
|
||||||
|
ptr: new(optionalPtrFieldNil),
|
||||||
|
value: optionalPtrFieldNil{A: 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: new(optionalPtrFieldNil),
|
||||||
|
error: "rlp: input string too short for [3]uint8, decoding into (rlp.optionalPtrFieldNil).B",
|
||||||
|
},
|
||||||
|
|
||||||
|
// struct tag "optional" field clearing
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: &optionalFields{A: 9, B: 8, C: 7},
|
||||||
|
value: optionalFields{A: 1, B: 0, C: 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: &optionalFields{A: 9, B: 8, C: 7},
|
||||||
|
value: optionalFields{A: 1, B: 2, C: 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C20102",
|
||||||
|
ptr: &optionalAndTailField{A: 9, B: 8, Tail: []uint{7, 6, 5}},
|
||||||
|
value: optionalAndTailField{A: 1, B: 2, Tail: []uint{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "C101",
|
||||||
|
ptr: &optionalPtrField{A: 9, B: &[3]byte{8, 7, 6}},
|
||||||
|
value: optionalPtrField{A: 1},
|
||||||
},
|
},
|
||||||
|
|
||||||
// RawValue
|
// RawValue
|
||||||
|
|
@ -591,6 +910,26 @@ func TestDecodeWithByteReader(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDecodeWithEncReader(t *testing.T, n int) {
|
||||||
|
s := strings.Repeat("0", n)
|
||||||
|
_, r, _ := EncodeToReader(s)
|
||||||
|
var decoded string
|
||||||
|
err := Decode(r, &decoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected decode error with n=%v: %v", n, err)
|
||||||
|
}
|
||||||
|
if decoded != s {
|
||||||
|
t.Errorf("Decode mismatch with n=%v", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a regression test checking that decoding from encReader
|
||||||
|
// works for RLP values of size 8192 bytes or more.
|
||||||
|
func TestDecodeWithEncReader(t *testing.T) {
|
||||||
|
testDecodeWithEncReader(t, 8188) // length with header is 8191
|
||||||
|
testDecodeWithEncReader(t, 8189) // length with header is 8192
|
||||||
|
}
|
||||||
|
|
||||||
// plainReader reads from a byte slice but does not
|
// plainReader reads from a byte slice but does not
|
||||||
// implement ReadByte. It is also not recognized by the
|
// implement ReadByte. It is also not recognized by the
|
||||||
// size validation. This is useful to test how the decoder
|
// size validation. This is useful to test how the decoder
|
||||||
|
|
@ -661,6 +1000,22 @@ func TestDecodeDecoder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecodeDecoderNilPointer(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
T1 *testDecoder `rlp:"nil"`
|
||||||
|
T2 *testDecoder
|
||||||
|
}
|
||||||
|
if err := Decode(bytes.NewReader(unhex("C2C002")), &s); err != nil {
|
||||||
|
t.Fatalf("Decode error: %v", err)
|
||||||
|
}
|
||||||
|
if s.T1 != nil {
|
||||||
|
t.Errorf("decoder T1 allocated for empty input (called: %v)", s.T1.called)
|
||||||
|
}
|
||||||
|
if s.T2 == nil || !s.T2.called {
|
||||||
|
t.Errorf("decoder T2 not allocated/called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type byteDecoder byte
|
type byteDecoder byte
|
||||||
|
|
||||||
func (bd *byteDecoder) DecodeRLP(s *Stream) error {
|
func (bd *byteDecoder) DecodeRLP(s *Stream) error {
|
||||||
|
|
@ -691,13 +1046,66 @@ func TestDecoderInByteSlice(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unencodableDecoder func()
|
||||||
|
|
||||||
|
func (f *unencodableDecoder) DecodeRLP(s *Stream) error {
|
||||||
|
if _, err := s.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*f = func() {}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecoderFunc(t *testing.T) {
|
||||||
|
var x func()
|
||||||
|
if err := DecodeBytes([]byte{0xC0}, (*unencodableDecoder)(&x)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
x()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This tests the validity checks for fields with struct tag "optional".
|
||||||
|
func TestInvalidOptionalField(t *testing.T) {
|
||||||
|
type (
|
||||||
|
invalid1 struct {
|
||||||
|
A uint `rlp:"optional"`
|
||||||
|
B uint
|
||||||
|
}
|
||||||
|
invalid2 struct {
|
||||||
|
T []uint `rlp:"tail,optional"`
|
||||||
|
}
|
||||||
|
invalid3 struct {
|
||||||
|
T []uint `rlp:"optional,tail"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
v interface{}
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{v: new(invalid1), err: `rlp: invalid struct tag "" for rlp.invalid1.B (must be optional because preceding field "A" is optional)`},
|
||||||
|
{v: new(invalid2), err: `rlp: invalid struct tag "optional" for rlp.invalid2.T (also has "tail" tag)`},
|
||||||
|
{v: new(invalid3), err: `rlp: invalid struct tag "tail" for rlp.invalid3.T (also has "optional" tag)`},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
err := DecodeBytes(unhex("C20102"), test.v)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("no error for %T", test.v)
|
||||||
|
} else if err.Error() != test.err {
|
||||||
|
t.Errorf("wrong error for %T: %v", test.v, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleDecode() {
|
func ExampleDecode() {
|
||||||
input, _ := hex.DecodeString("C90A1486666F6F626172")
|
input, _ := hex.DecodeString("C90A1486666F6F626172")
|
||||||
|
|
||||||
type example struct {
|
type example struct {
|
||||||
A, B uint
|
A, B uint
|
||||||
private uint // private fields are ignored
|
String string
|
||||||
String string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var s example
|
var s example
|
||||||
|
|
@ -708,7 +1116,7 @@ func ExampleDecode() {
|
||||||
fmt.Printf("Decoded value: %#v\n", s)
|
fmt.Printf("Decoded value: %#v\n", s)
|
||||||
}
|
}
|
||||||
// Output:
|
// Output:
|
||||||
// Decoded value: rlp.example{A:0xa, B:0x14, private:0x0, String:"foobar"}
|
// Decoded value: rlp.example{A:0xa, B:0x14, String:"foobar"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDecode_structTagNil() {
|
func ExampleDecode_structTagNil() {
|
||||||
|
|
@ -768,7 +1176,7 @@ func ExampleStream() {
|
||||||
// [102 111 111 98 97 114] <nil>
|
// [102 111 111 98 97 114] <nil>
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDecode(b *testing.B) {
|
func BenchmarkDecodeUints(b *testing.B) {
|
||||||
enc := encodeTestSlice(90000)
|
enc := encodeTestSlice(90000)
|
||||||
b.SetBytes(int64(len(enc)))
|
b.SetBytes(int64(len(enc)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
@ -783,7 +1191,7 @@ func BenchmarkDecode(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDecodeIntSliceReuse(b *testing.B) {
|
func BenchmarkDecodeUintsReused(b *testing.B) {
|
||||||
enc := encodeTestSlice(100000)
|
enc := encodeTestSlice(100000)
|
||||||
b.SetBytes(int64(len(enc)))
|
b.SetBytes(int64(len(enc)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
@ -798,6 +1206,65 @@ func BenchmarkDecodeIntSliceReuse(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeByteArrayStruct(b *testing.B) {
|
||||||
|
enc, err := EncodeToBytes(&byteArrayStruct{})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(enc)))
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var out byteArrayStruct
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := DecodeBytes(enc, &out); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeBigInts(b *testing.B) {
|
||||||
|
ints := make([]*big.Int, 200)
|
||||||
|
for i := range ints {
|
||||||
|
ints[i] = math.BigPow(2, int64(i))
|
||||||
|
}
|
||||||
|
enc, err := EncodeToBytes(ints)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(enc)))
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var out []*big.Int
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := DecodeBytes(enc, &out); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeU256Ints(b *testing.B) {
|
||||||
|
ints := make([]*uint256.Int, 200)
|
||||||
|
for i := range ints {
|
||||||
|
ints[i], _ = uint256.FromBig(math.BigPow(2, int64(i)))
|
||||||
|
}
|
||||||
|
enc, err := EncodeToBytes(ints)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(enc)))
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var out []*uint256.Int
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := DecodeBytes(enc, &out); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encodeTestSlice(n uint) []byte {
|
func encodeTestSlice(n uint) []byte {
|
||||||
s := make([]uint, n)
|
s := make([]uint, n)
|
||||||
for i := uint(0); i < n; i++ {
|
for i := uint(0); i < n; i++ {
|
||||||
|
|
@ -811,7 +1278,7 @@ func encodeTestSlice(n uint) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func unhex(str string) []byte {
|
func unhex(str string) []byte {
|
||||||
b, err := hex.DecodeString(strings.Replace(str, " ", "", -1))
|
b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("invalid hex string: %q", str))
|
panic(fmt.Sprintf("invalid hex string: %q", str))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
147
rlp/doc.go
147
rlp/doc.go
|
|
@ -17,17 +17,142 @@
|
||||||
/*
|
/*
|
||||||
Package rlp implements the RLP serialization format.
|
Package rlp implements the RLP serialization format.
|
||||||
|
|
||||||
The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily
|
The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily nested arrays of
|
||||||
nested arrays of binary data, and RLP is the main encoding method used
|
binary data, and RLP is the main encoding method used to serialize objects in Ethereum.
|
||||||
to serialize objects in Ethereum. The only purpose of RLP is to encode
|
The only purpose of RLP is to encode structure; encoding specific atomic data types (eg.
|
||||||
structure; encoding specific atomic data types (eg. strings, ints,
|
strings, ints, floats) is left up to higher-order protocols. In Ethereum integers must be
|
||||||
floats) is left up to higher-order protocols; in Ethereum integers
|
represented in big endian binary form with no leading zeroes (thus making the integer
|
||||||
must be represented in big endian binary form with no leading zeroes
|
value zero equivalent to the empty string).
|
||||||
(thus making the integer value zero equivalent to the empty byte
|
|
||||||
array).
|
|
||||||
|
|
||||||
RLP values are distinguished by a type tag. The type tag precedes the
|
RLP values are distinguished by a type tag. The type tag precedes the value in the input
|
||||||
value in the input stream and defines the size and kind of the bytes
|
stream and defines the size and kind of the bytes that follow.
|
||||||
that follow.
|
|
||||||
|
# Encoding Rules
|
||||||
|
|
||||||
|
Package rlp uses reflection and encodes RLP based on the Go type of the value.
|
||||||
|
|
||||||
|
If the type implements the Encoder interface, Encode calls EncodeRLP. It does not
|
||||||
|
call EncodeRLP on nil pointer values.
|
||||||
|
|
||||||
|
To encode a pointer, the value being pointed to is encoded. A nil pointer to a struct
|
||||||
|
type, slice or array always encodes as an empty RLP list unless the slice or array has
|
||||||
|
element type byte. A nil pointer to any other value encodes as the empty string.
|
||||||
|
|
||||||
|
Struct values are encoded as an RLP list of all their encoded public fields. Recursive
|
||||||
|
struct types are supported.
|
||||||
|
|
||||||
|
To encode slices and arrays, the elements are encoded as an RLP list of the value's
|
||||||
|
elements. Note that arrays and slices with element type uint8 or byte are always encoded
|
||||||
|
as an RLP string.
|
||||||
|
|
||||||
|
A Go string is encoded as an RLP string.
|
||||||
|
|
||||||
|
An unsigned integer value is encoded as an RLP string. Zero always encodes as an empty RLP
|
||||||
|
string. big.Int values are treated as integers. Signed integers (int, int8, int16, ...)
|
||||||
|
are not supported and will return an error when encoding.
|
||||||
|
|
||||||
|
Boolean values are encoded as the unsigned integers zero (false) and one (true).
|
||||||
|
|
||||||
|
An interface value encodes as the value contained in the interface.
|
||||||
|
|
||||||
|
Floating point numbers, maps, channels and functions are not supported.
|
||||||
|
|
||||||
|
# Decoding Rules
|
||||||
|
|
||||||
|
Decoding uses the following type-dependent rules:
|
||||||
|
|
||||||
|
If the type implements the Decoder interface, DecodeRLP is called.
|
||||||
|
|
||||||
|
To decode into a pointer, the value will be decoded as the element type of the pointer. If
|
||||||
|
the pointer is nil, a new value of the pointer's element type is allocated. If the pointer
|
||||||
|
is non-nil, the existing value will be reused. Note that package rlp never leaves a
|
||||||
|
pointer-type struct field as nil unless one of the "nil" struct tags is present.
|
||||||
|
|
||||||
|
To decode into a struct, decoding expects the input to be an RLP list. The decoded
|
||||||
|
elements of the list are assigned to each public field in the order given by the struct's
|
||||||
|
definition. The input list must contain an element for each decoded field. Decoding
|
||||||
|
returns an error if there are too few or too many elements for the struct.
|
||||||
|
|
||||||
|
To decode into a slice, the input must be a list and the resulting slice will contain the
|
||||||
|
input elements in order. For byte slices, the input must be an RLP string. Array types
|
||||||
|
decode similarly, with the additional restriction that the number of input elements (or
|
||||||
|
bytes) must match the array's defined length.
|
||||||
|
|
||||||
|
To decode into a Go string, the input must be an RLP string. The input bytes are taken
|
||||||
|
as-is and will not necessarily be valid UTF-8.
|
||||||
|
|
||||||
|
To decode into an unsigned integer type, the input must also be an RLP string. The bytes
|
||||||
|
are interpreted as a big endian representation of the integer. If the RLP string is larger
|
||||||
|
than the bit size of the type, decoding will return an error. Decode also supports
|
||||||
|
*big.Int. There is no size limit for big integers.
|
||||||
|
|
||||||
|
To decode into a boolean, the input must contain an unsigned integer of value zero (false)
|
||||||
|
or one (true).
|
||||||
|
|
||||||
|
To decode into an interface value, one of these types is stored in the value:
|
||||||
|
|
||||||
|
[]interface{}, for RLP lists
|
||||||
|
[]byte, for RLP strings
|
||||||
|
|
||||||
|
Non-empty interface types are not supported when decoding.
|
||||||
|
Signed integers, floating point numbers, maps, channels and functions cannot be decoded into.
|
||||||
|
|
||||||
|
# Struct Tags
|
||||||
|
|
||||||
|
As with other encoding packages, the "-" tag ignores fields.
|
||||||
|
|
||||||
|
type StructWithIgnoredField struct{
|
||||||
|
Ignored uint `rlp:"-"`
|
||||||
|
Field uint
|
||||||
|
}
|
||||||
|
|
||||||
|
Go struct values encode/decode as RLP lists. There are two ways of influencing the mapping
|
||||||
|
of fields to list elements. The "tail" tag, which may only be used on the last exported
|
||||||
|
struct field, allows slurping up any excess list elements into a slice.
|
||||||
|
|
||||||
|
type StructWithTail struct{
|
||||||
|
Field uint
|
||||||
|
Tail []string `rlp:"tail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
The "optional" tag says that the field may be omitted if it is zero-valued. If this tag is
|
||||||
|
used on a struct field, all subsequent public fields must also be declared optional.
|
||||||
|
|
||||||
|
When encoding a struct with optional fields, the output RLP list contains all values up to
|
||||||
|
the last non-zero optional field.
|
||||||
|
|
||||||
|
When decoding into a struct, optional fields may be omitted from the end of the input
|
||||||
|
list. For the example below, this means input lists of one, two, or three elements are
|
||||||
|
accepted.
|
||||||
|
|
||||||
|
type StructWithOptionalFields struct{
|
||||||
|
Required uint
|
||||||
|
Optional1 uint `rlp:"optional"`
|
||||||
|
Optional2 uint `rlp:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
The "nil", "nilList" and "nilString" tags apply to pointer-typed fields only, and change
|
||||||
|
the decoding rules for the field type. For regular pointer fields without the "nil" tag,
|
||||||
|
input values must always match the required input length exactly and the decoder does not
|
||||||
|
produce nil values. When the "nil" tag is set, input values of size zero decode as a nil
|
||||||
|
pointer. This is especially useful for recursive types.
|
||||||
|
|
||||||
|
type StructWithNilField struct {
|
||||||
|
Field *[3]byte `rlp:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
In the example above, Field allows two possible input sizes. For input 0xC180 (a list
|
||||||
|
containing an empty string) Field is set to nil after decoding. For input 0xC483000000 (a
|
||||||
|
list containing a 3-byte string), Field is set to a non-nil array pointer.
|
||||||
|
|
||||||
|
RLP supports two kinds of empty values: empty lists and empty strings. When using the
|
||||||
|
"nil" tag, the kind of empty value allowed for a type is chosen automatically. A field
|
||||||
|
whose Go type is a pointer to an unsigned integer, string, boolean or byte array/slice
|
||||||
|
expects an empty RLP string. Any other pointer field type encodes/decodes as an empty RLP
|
||||||
|
list.
|
||||||
|
|
||||||
|
The choice of null value can be made explicit with the "nilList" and "nilString" struct
|
||||||
|
tags. Using these tags encodes/decodes a Go nil pointer value as the empty RLP value kind
|
||||||
|
defined by the tag.
|
||||||
*/
|
*/
|
||||||
package rlp
|
package rlp
|
||||||
|
|
|
||||||
423
rlp/encbuffer.go
Normal file
423
rlp/encbuffer.go
Normal file
|
|
@ -0,0 +1,423 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package rlp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encBuffer struct {
|
||||||
|
str []byte // string data, contains everything except list headers
|
||||||
|
lheads []listhead // all list headers
|
||||||
|
lhsize int // sum of sizes of all encoded list headers
|
||||||
|
sizebuf [9]byte // auxiliary buffer for uint encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// The global encBuffer pool.
|
||||||
|
var encBufferPool = sync.Pool{
|
||||||
|
New: func() interface{} { return new(encBuffer) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEncBuffer() *encBuffer {
|
||||||
|
buf := encBufferPool.Get().(*encBuffer)
|
||||||
|
buf.reset()
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) reset() {
|
||||||
|
buf.lhsize = 0
|
||||||
|
buf.str = buf.str[:0]
|
||||||
|
buf.lheads = buf.lheads[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// size returns the length of the encoded data.
|
||||||
|
func (buf *encBuffer) size() int {
|
||||||
|
return len(buf.str) + buf.lhsize
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeBytes creates the encoder output.
|
||||||
|
func (buf *encBuffer) makeBytes() []byte {
|
||||||
|
out := make([]byte, buf.size())
|
||||||
|
buf.copyTo(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) copyTo(dst []byte) {
|
||||||
|
strpos := 0
|
||||||
|
pos := 0
|
||||||
|
for _, head := range buf.lheads {
|
||||||
|
// write string data before header
|
||||||
|
n := copy(dst[pos:], buf.str[strpos:head.offset])
|
||||||
|
pos += n
|
||||||
|
strpos += n
|
||||||
|
// write the header
|
||||||
|
enc := head.encode(dst[pos:])
|
||||||
|
pos += len(enc)
|
||||||
|
}
|
||||||
|
// copy string data after the last list header
|
||||||
|
copy(dst[pos:], buf.str[strpos:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTo writes the encoder output to w.
|
||||||
|
func (buf *encBuffer) writeTo(w io.Writer) (err error) {
|
||||||
|
strpos := 0
|
||||||
|
for _, head := range buf.lheads {
|
||||||
|
// write string data before header
|
||||||
|
if head.offset-strpos > 0 {
|
||||||
|
n, err := w.Write(buf.str[strpos:head.offset])
|
||||||
|
strpos += n
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write the header
|
||||||
|
enc := head.encode(buf.sizebuf[:])
|
||||||
|
if _, err = w.Write(enc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strpos < len(buf.str) {
|
||||||
|
// write string data after the last list header
|
||||||
|
_, err = w.Write(buf.str[strpos:])
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements io.Writer and appends b directly to the output.
|
||||||
|
func (buf *encBuffer) Write(b []byte) (int, error) {
|
||||||
|
buf.str = append(buf.str, b...)
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBool writes b as the integer 0 (false) or 1 (true).
|
||||||
|
func (buf *encBuffer) writeBool(b bool) {
|
||||||
|
if b {
|
||||||
|
buf.str = append(buf.str, 0x01)
|
||||||
|
} else {
|
||||||
|
buf.str = append(buf.str, 0x80)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) writeUint64(i uint64) {
|
||||||
|
if i == 0 {
|
||||||
|
buf.str = append(buf.str, 0x80)
|
||||||
|
} else if i < 128 {
|
||||||
|
// fits single byte
|
||||||
|
buf.str = append(buf.str, byte(i))
|
||||||
|
} else {
|
||||||
|
s := putint(buf.sizebuf[1:], i)
|
||||||
|
buf.sizebuf[0] = 0x80 + byte(s)
|
||||||
|
buf.str = append(buf.str, buf.sizebuf[:s+1]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) writeBytes(b []byte) {
|
||||||
|
if len(b) == 1 && b[0] <= 0x7F {
|
||||||
|
// fits single byte, no string header
|
||||||
|
buf.str = append(buf.str, b[0])
|
||||||
|
} else {
|
||||||
|
buf.encodeStringHeader(len(b))
|
||||||
|
buf.str = append(buf.str, b...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) writeString(s string) {
|
||||||
|
buf.writeBytes([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// wordBytes is the number of bytes in a big.Word
|
||||||
|
const wordBytes = (32 << (uint64(^big.Word(0)) >> 63)) / 8
|
||||||
|
|
||||||
|
// writeBigInt writes i as an integer.
|
||||||
|
func (buf *encBuffer) writeBigInt(i *big.Int) {
|
||||||
|
bitlen := i.BitLen()
|
||||||
|
if bitlen <= 64 {
|
||||||
|
buf.writeUint64(i.Uint64())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Integer is larger than 64 bits, encode from i.Bits().
|
||||||
|
// The minimal byte length is bitlen rounded up to the next
|
||||||
|
// multiple of 8, divided by 8.
|
||||||
|
length := ((bitlen + 7) & -8) >> 3
|
||||||
|
buf.encodeStringHeader(length)
|
||||||
|
buf.str = append(buf.str, make([]byte, length)...)
|
||||||
|
index := length
|
||||||
|
bytesBuf := buf.str[len(buf.str)-length:]
|
||||||
|
for _, d := range i.Bits() {
|
||||||
|
for j := 0; j < wordBytes && index > 0; j++ {
|
||||||
|
index--
|
||||||
|
bytesBuf[index] = byte(d)
|
||||||
|
d >>= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeUint256 writes z as an integer.
|
||||||
|
func (buf *encBuffer) writeUint256(z *uint256.Int) {
|
||||||
|
bitlen := z.BitLen()
|
||||||
|
if bitlen <= 64 {
|
||||||
|
buf.writeUint64(z.Uint64())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nBytes := byte((bitlen + 7) / 8)
|
||||||
|
var b [33]byte
|
||||||
|
binary.BigEndian.PutUint64(b[1:9], z[3])
|
||||||
|
binary.BigEndian.PutUint64(b[9:17], z[2])
|
||||||
|
binary.BigEndian.PutUint64(b[17:25], z[1])
|
||||||
|
binary.BigEndian.PutUint64(b[25:33], z[0])
|
||||||
|
b[32-nBytes] = 0x80 + nBytes
|
||||||
|
buf.str = append(buf.str, b[32-nBytes:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list adds a new list header to the header stack. It returns the index of the header.
|
||||||
|
// Call listEnd with this index after encoding the content of the list.
|
||||||
|
func (buf *encBuffer) list() int {
|
||||||
|
buf.lheads = append(buf.lheads, listhead{offset: len(buf.str), size: buf.lhsize})
|
||||||
|
return len(buf.lheads) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) listEnd(index int) {
|
||||||
|
lh := &buf.lheads[index]
|
||||||
|
lh.size = buf.size() - lh.offset - lh.size
|
||||||
|
if lh.size < 56 {
|
||||||
|
buf.lhsize++ // length encoded into kind tag
|
||||||
|
} else {
|
||||||
|
buf.lhsize += 1 + intsize(uint64(lh.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) encode(val interface{}) error {
|
||||||
|
rval := reflect.ValueOf(val)
|
||||||
|
writer, err := cachedWriter(rval.Type())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writer(rval, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *encBuffer) encodeStringHeader(size int) {
|
||||||
|
if size < 56 {
|
||||||
|
buf.str = append(buf.str, 0x80+byte(size))
|
||||||
|
} else {
|
||||||
|
sizesize := putint(buf.sizebuf[1:], uint64(size))
|
||||||
|
buf.sizebuf[0] = 0xB7 + byte(sizesize)
|
||||||
|
buf.str = append(buf.str, buf.sizebuf[:sizesize+1]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encReader is the io.Reader returned by EncodeToReader.
|
||||||
|
// It releases its encbuf at EOF.
|
||||||
|
type encReader struct {
|
||||||
|
buf *encBuffer // the buffer we're reading from. this is nil when we're at EOF.
|
||||||
|
lhpos int // index of list header that we're reading
|
||||||
|
strpos int // current position in string buffer
|
||||||
|
piece []byte // next piece to be read
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *encReader) Read(b []byte) (n int, err error) {
|
||||||
|
for {
|
||||||
|
if r.piece = r.next(); r.piece == nil {
|
||||||
|
// Put the encode buffer back into the pool at EOF when it
|
||||||
|
// is first encountered. Subsequent calls still return EOF
|
||||||
|
// as the error but the buffer is no longer valid.
|
||||||
|
if r.buf != nil {
|
||||||
|
encBufferPool.Put(r.buf)
|
||||||
|
r.buf = nil
|
||||||
|
}
|
||||||
|
return n, io.EOF
|
||||||
|
}
|
||||||
|
nn := copy(b[n:], r.piece)
|
||||||
|
n += nn
|
||||||
|
if nn < len(r.piece) {
|
||||||
|
// piece didn't fit, see you next time.
|
||||||
|
r.piece = r.piece[nn:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
r.piece = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// next returns the next piece of data to be read.
|
||||||
|
// it returns nil at EOF.
|
||||||
|
func (r *encReader) next() []byte {
|
||||||
|
switch {
|
||||||
|
case r.buf == nil:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case r.piece != nil:
|
||||||
|
// There is still data available for reading.
|
||||||
|
return r.piece
|
||||||
|
|
||||||
|
case r.lhpos < len(r.buf.lheads):
|
||||||
|
// We're before the last list header.
|
||||||
|
head := r.buf.lheads[r.lhpos]
|
||||||
|
sizebefore := head.offset - r.strpos
|
||||||
|
if sizebefore > 0 {
|
||||||
|
// String data before header.
|
||||||
|
p := r.buf.str[r.strpos:head.offset]
|
||||||
|
r.strpos += sizebefore
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
r.lhpos++
|
||||||
|
return head.encode(r.buf.sizebuf[:])
|
||||||
|
|
||||||
|
case r.strpos < len(r.buf.str):
|
||||||
|
// String data at the end, after all list headers.
|
||||||
|
p := r.buf.str[r.strpos:]
|
||||||
|
r.strpos = len(r.buf.str)
|
||||||
|
return p
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encBufferFromWriter(w io.Writer) *encBuffer {
|
||||||
|
switch w := w.(type) {
|
||||||
|
case EncoderBuffer:
|
||||||
|
return w.buf
|
||||||
|
case *EncoderBuffer:
|
||||||
|
return w.buf
|
||||||
|
case *encBuffer:
|
||||||
|
return w
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncoderBuffer is a buffer for incremental encoding.
|
||||||
|
//
|
||||||
|
// The zero value is NOT ready for use. To get a usable buffer,
|
||||||
|
// create it using NewEncoderBuffer or call Reset.
|
||||||
|
type EncoderBuffer struct {
|
||||||
|
buf *encBuffer
|
||||||
|
dst io.Writer
|
||||||
|
|
||||||
|
ownBuffer bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoderBuffer creates an encoder buffer.
|
||||||
|
func NewEncoderBuffer(dst io.Writer) EncoderBuffer {
|
||||||
|
var w EncoderBuffer
|
||||||
|
w.Reset(dst)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset truncates the buffer and sets the output destination.
|
||||||
|
func (w *EncoderBuffer) Reset(dst io.Writer) {
|
||||||
|
if w.buf != nil && !w.ownBuffer {
|
||||||
|
panic("can't Reset derived EncoderBuffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the destination writer has an *encBuffer, use it.
|
||||||
|
// Note that w.ownBuffer is left false here.
|
||||||
|
if dst != nil {
|
||||||
|
if outer := encBufferFromWriter(dst); outer != nil {
|
||||||
|
*w = EncoderBuffer{outer, nil, false}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a fresh buffer.
|
||||||
|
if w.buf == nil {
|
||||||
|
w.buf = encBufferPool.Get().(*encBuffer)
|
||||||
|
w.ownBuffer = true
|
||||||
|
}
|
||||||
|
w.buf.reset()
|
||||||
|
w.dst = dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush writes encoded RLP data to the output writer. This can only be called once.
|
||||||
|
// If you want to re-use the buffer after Flush, you must call Reset.
|
||||||
|
func (w *EncoderBuffer) Flush() error {
|
||||||
|
var err error
|
||||||
|
if w.dst != nil {
|
||||||
|
err = w.buf.writeTo(w.dst)
|
||||||
|
}
|
||||||
|
// Release the internal buffer.
|
||||||
|
if w.ownBuffer {
|
||||||
|
encBufferPool.Put(w.buf)
|
||||||
|
}
|
||||||
|
*w = EncoderBuffer{}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBytes returns the encoded bytes.
|
||||||
|
func (w *EncoderBuffer) ToBytes() []byte {
|
||||||
|
return w.buf.makeBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendToBytes appends the encoded bytes to dst.
|
||||||
|
func (w *EncoderBuffer) AppendToBytes(dst []byte) []byte {
|
||||||
|
size := w.buf.size()
|
||||||
|
out := append(dst, make([]byte, size)...)
|
||||||
|
w.buf.copyTo(out[len(dst):])
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write appends b directly to the encoder output.
|
||||||
|
func (w EncoderBuffer) Write(b []byte) (int, error) {
|
||||||
|
return w.buf.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBool writes b as the integer 0 (false) or 1 (true).
|
||||||
|
func (w EncoderBuffer) WriteBool(b bool) {
|
||||||
|
w.buf.writeBool(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteUint64 encodes an unsigned integer.
|
||||||
|
func (w EncoderBuffer) WriteUint64(i uint64) {
|
||||||
|
w.buf.writeUint64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBigInt encodes a big.Int as an RLP string.
|
||||||
|
// Note: Unlike with Encode, the sign of i is ignored.
|
||||||
|
func (w EncoderBuffer) WriteBigInt(i *big.Int) {
|
||||||
|
w.buf.writeBigInt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteUint256 encodes uint256.Int as an RLP string.
|
||||||
|
func (w EncoderBuffer) WriteUint256(i *uint256.Int) {
|
||||||
|
w.buf.writeUint256(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBytes encodes b as an RLP string.
|
||||||
|
func (w EncoderBuffer) WriteBytes(b []byte) {
|
||||||
|
w.buf.writeBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteString encodes s as an RLP string.
|
||||||
|
func (w EncoderBuffer) WriteString(s string) {
|
||||||
|
w.buf.writeString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List starts a list. It returns an internal index. Call EndList with
|
||||||
|
// this index after encoding the content to finish the list.
|
||||||
|
func (w EncoderBuffer) List() int {
|
||||||
|
return w.buf.list()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEnd finishes the given list.
|
||||||
|
func (w EncoderBuffer) ListEnd(index int) {
|
||||||
|
w.buf.listEnd(index)
|
||||||
|
}
|
||||||
45
rlp/encbuffer_example_test.go
Normal file
45
rlp/encbuffer_example_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package rlp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleEncoderBuffer() {
|
||||||
|
var w bytes.Buffer
|
||||||
|
|
||||||
|
// Encode [4, [5, 6]] to w.
|
||||||
|
buf := rlp.NewEncoderBuffer(&w)
|
||||||
|
l1 := buf.List()
|
||||||
|
buf.WriteUint64(4)
|
||||||
|
l2 := buf.List()
|
||||||
|
buf.WriteUint64(5)
|
||||||
|
buf.WriteUint64(6)
|
||||||
|
buf.ListEnd(l2)
|
||||||
|
buf.ListEnd(l1)
|
||||||
|
|
||||||
|
if err := buf.Flush(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%X\n", w.Bytes())
|
||||||
|
// Output:
|
||||||
|
// C404C20506
|
||||||
|
}
|
||||||
617
rlp/encode.go
617
rlp/encode.go
|
|
@ -17,20 +17,28 @@
|
||||||
package rlp
|
package rlp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Common encoded values.
|
// Common encoded values.
|
||||||
// These are useful when implementing EncodeRLP.
|
// These are useful when implementing EncodeRLP.
|
||||||
|
|
||||||
|
// EmptyString is the encoding of an empty string.
|
||||||
EmptyString = []byte{0x80}
|
EmptyString = []byte{0x80}
|
||||||
EmptyList = []byte{0xC0}
|
// EmptyList is the encoding of an empty list.
|
||||||
|
EmptyList = []byte{0xC0}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNegativeBigInt = errors.New("rlp: cannot encode negative big.Int")
|
||||||
|
|
||||||
// Encoder is implemented by types that require custom
|
// Encoder is implemented by types that require custom
|
||||||
// encoding rules or want to encode private fields.
|
// encoding rules or want to encode private fields.
|
||||||
type Encoder interface {
|
type Encoder interface {
|
||||||
|
|
@ -49,80 +57,48 @@ type Encoder interface {
|
||||||
// perform many small writes in some cases. Consider making w
|
// perform many small writes in some cases. Consider making w
|
||||||
// buffered.
|
// buffered.
|
||||||
//
|
//
|
||||||
// Encode uses the following type-dependent encoding rules:
|
// Please see package-level documentation of encoding rules.
|
||||||
//
|
|
||||||
// If the type implements the Encoder interface, Encode calls
|
|
||||||
// EncodeRLP. This is true even for nil pointers, please see the
|
|
||||||
// documentation for Encoder.
|
|
||||||
//
|
|
||||||
// To encode a pointer, the value being pointed to is encoded. For nil
|
|
||||||
// pointers, Encode will encode the zero value of the type. A nil
|
|
||||||
// pointer to a struct type always encodes as an empty RLP list.
|
|
||||||
// A nil pointer to an array encodes as an empty list (or empty string
|
|
||||||
// if the array has element type byte).
|
|
||||||
//
|
|
||||||
// Struct values are encoded as an RLP list of all their encoded
|
|
||||||
// public fields. Recursive struct types are supported.
|
|
||||||
//
|
|
||||||
// To encode slices and arrays, the elements are encoded as an RLP
|
|
||||||
// list of the value's elements. Note that arrays and slices with
|
|
||||||
// element type uint8 or byte are always encoded as an RLP string.
|
|
||||||
//
|
|
||||||
// A Go string is encoded as an RLP string.
|
|
||||||
//
|
|
||||||
// An unsigned integer value is encoded as an RLP string. Zero always
|
|
||||||
// encodes as an empty RLP string. Encode also supports *big.Int.
|
|
||||||
//
|
|
||||||
// An interface value encodes as the value contained in the interface.
|
|
||||||
//
|
|
||||||
// Boolean values are not supported, nor are signed integers, floating
|
|
||||||
// point numbers, maps, channels and functions.
|
|
||||||
func Encode(w io.Writer, val interface{}) error {
|
func Encode(w io.Writer, val interface{}) error {
|
||||||
if outer, ok := w.(*encbuf); ok {
|
// Optimization: reuse *encBuffer when called by EncodeRLP.
|
||||||
// Encode was called by some type's EncodeRLP.
|
if buf := encBufferFromWriter(w); buf != nil {
|
||||||
// Avoid copying by writing to the outer encbuf directly.
|
return buf.encode(val)
|
||||||
return outer.encode(val)
|
|
||||||
}
|
}
|
||||||
eb := encbufPool.Get().(*encbuf)
|
|
||||||
defer encbufPool.Put(eb)
|
buf := getEncBuffer()
|
||||||
eb.reset()
|
defer encBufferPool.Put(buf)
|
||||||
if err := eb.encode(val); err != nil {
|
if err := buf.encode(val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return eb.toWriter(w)
|
return buf.writeTo(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBytes returns the RLP encoding of val.
|
// EncodeToBytes returns the RLP encoding of val.
|
||||||
// Please see the documentation of Encode for the encoding rules.
|
// Please see package-level documentation for the encoding rules.
|
||||||
func EncodeToBytes(val interface{}) ([]byte, error) {
|
func EncodeToBytes(val interface{}) ([]byte, error) {
|
||||||
eb := encbufPool.Get().(*encbuf)
|
buf := getEncBuffer()
|
||||||
defer encbufPool.Put(eb)
|
defer encBufferPool.Put(buf)
|
||||||
eb.reset()
|
|
||||||
if err := eb.encode(val); err != nil {
|
if err := buf.encode(val); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return eb.toBytes(), nil
|
return buf.makeBytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeReader returns a reader from which the RLP encoding of val
|
// EncodeToReader returns a reader from which the RLP encoding of val
|
||||||
// can be read. The returned size is the total size of the encoded
|
// can be read. The returned size is the total size of the encoded
|
||||||
// data.
|
// data.
|
||||||
//
|
//
|
||||||
// Please see the documentation of Encode for the encoding rules.
|
// Please see the documentation of Encode for the encoding rules.
|
||||||
func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
|
func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
|
||||||
eb := encbufPool.Get().(*encbuf)
|
buf := getEncBuffer()
|
||||||
eb.reset()
|
if err := buf.encode(val); err != nil {
|
||||||
if err := eb.encode(val); err != nil {
|
encBufferPool.Put(buf)
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
return eb.size(), &encReader{buf: eb}, nil
|
// Note: can't put the reader back into the pool here
|
||||||
}
|
// because it is held by encReader. The reader puts it
|
||||||
|
// back when it has been fully consumed.
|
||||||
type encbuf struct {
|
return buf.size(), &encReader{buf: buf}, nil
|
||||||
str []byte // string data, contains everything except list headers
|
|
||||||
lheads []*listhead // all list headers
|
|
||||||
lhsize int // sum of sizes of all encoded list headers
|
|
||||||
sizebuf []byte // 9-byte auxiliary buffer for uint encoding
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type listhead struct {
|
type listhead struct {
|
||||||
|
|
@ -151,214 +127,32 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
|
||||||
if size < 56 {
|
if size < 56 {
|
||||||
buf[0] = smalltag + byte(size)
|
buf[0] = smalltag + byte(size)
|
||||||
return 1
|
return 1
|
||||||
} else {
|
|
||||||
sizesize := putint(buf[1:], size)
|
|
||||||
buf[0] = largetag + byte(sizesize)
|
|
||||||
return sizesize + 1
|
|
||||||
}
|
}
|
||||||
|
sizesize := putint(buf[1:], size)
|
||||||
|
buf[0] = largetag + byte(sizesize)
|
||||||
|
return sizesize + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// encbufs are pooled.
|
var encoderInterface = reflect.TypeOf(new(Encoder)).Elem()
|
||||||
var encbufPool = sync.Pool{
|
|
||||||
New: func() interface{} { return &encbuf{sizebuf: make([]byte, 9)} },
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) reset() {
|
|
||||||
w.lhsize = 0
|
|
||||||
if w.str != nil {
|
|
||||||
w.str = w.str[:0]
|
|
||||||
}
|
|
||||||
if w.lheads != nil {
|
|
||||||
w.lheads = w.lheads[:0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// encbuf implements io.Writer so it can be passed it into EncodeRLP.
|
|
||||||
func (w *encbuf) Write(b []byte) (int, error) {
|
|
||||||
w.str = append(w.str, b...)
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) encode(val interface{}) error {
|
|
||||||
rval := reflect.ValueOf(val)
|
|
||||||
ti, err := cachedTypeInfo(rval.Type(), tags{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ti.writer(rval, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) encodeStringHeader(size int) {
|
|
||||||
if size < 56 {
|
|
||||||
w.str = append(w.str, 0x80+byte(size))
|
|
||||||
} else {
|
|
||||||
// TODO: encode to w.str directly
|
|
||||||
sizesize := putint(w.sizebuf[1:], uint64(size))
|
|
||||||
w.sizebuf[0] = 0xB7 + byte(sizesize)
|
|
||||||
w.str = append(w.str, w.sizebuf[:sizesize+1]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) encodeString(b []byte) {
|
|
||||||
if len(b) == 1 && b[0] <= 0x7F {
|
|
||||||
// fits single byte, no string header
|
|
||||||
w.str = append(w.str, b[0])
|
|
||||||
} else {
|
|
||||||
w.encodeStringHeader(len(b))
|
|
||||||
w.str = append(w.str, b...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) list() *listhead {
|
|
||||||
lh := &listhead{offset: len(w.str), size: w.lhsize}
|
|
||||||
w.lheads = append(w.lheads, lh)
|
|
||||||
return lh
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) listEnd(lh *listhead) {
|
|
||||||
lh.size = w.size() - lh.offset - lh.size
|
|
||||||
if lh.size < 56 {
|
|
||||||
w.lhsize += 1 // length encoded into kind tag
|
|
||||||
} else {
|
|
||||||
w.lhsize += 1 + intsize(uint64(lh.size))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) size() int {
|
|
||||||
return len(w.str) + w.lhsize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) toBytes() []byte {
|
|
||||||
out := make([]byte, w.size())
|
|
||||||
strpos := 0
|
|
||||||
pos := 0
|
|
||||||
for _, head := range w.lheads {
|
|
||||||
// write string data before header
|
|
||||||
n := copy(out[pos:], w.str[strpos:head.offset])
|
|
||||||
pos += n
|
|
||||||
strpos += n
|
|
||||||
// write the header
|
|
||||||
enc := head.encode(out[pos:])
|
|
||||||
pos += len(enc)
|
|
||||||
}
|
|
||||||
// copy string data after the last list header
|
|
||||||
copy(out[pos:], w.str[strpos:])
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *encbuf) toWriter(out io.Writer) (err error) {
|
|
||||||
strpos := 0
|
|
||||||
for _, head := range w.lheads {
|
|
||||||
// write string data before header
|
|
||||||
if head.offset-strpos > 0 {
|
|
||||||
n, err := out.Write(w.str[strpos:head.offset])
|
|
||||||
strpos += n
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// write the header
|
|
||||||
enc := head.encode(w.sizebuf)
|
|
||||||
if _, err = out.Write(enc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strpos < len(w.str) {
|
|
||||||
// write string data after the last list header
|
|
||||||
_, err = out.Write(w.str[strpos:])
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// encReader is the io.Reader returned by EncodeToReader.
|
|
||||||
// It releases its encbuf at EOF.
|
|
||||||
type encReader struct {
|
|
||||||
buf *encbuf // the buffer we're reading from. this is nil when we're at EOF.
|
|
||||||
lhpos int // index of list header that we're reading
|
|
||||||
strpos int // current position in string buffer
|
|
||||||
piece []byte // next piece to be read
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *encReader) Read(b []byte) (n int, err error) {
|
|
||||||
for {
|
|
||||||
if r.piece = r.next(); r.piece == nil {
|
|
||||||
// Put the encode buffer back into the pool at EOF when it
|
|
||||||
// is first encountered. Subsequent calls still return EOF
|
|
||||||
// as the error but the buffer is no longer valid.
|
|
||||||
if r.buf != nil {
|
|
||||||
encbufPool.Put(r.buf)
|
|
||||||
r.buf = nil
|
|
||||||
}
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
nn := copy(b[n:], r.piece)
|
|
||||||
n += nn
|
|
||||||
if nn < len(r.piece) {
|
|
||||||
// piece didn't fit, see you next time.
|
|
||||||
r.piece = r.piece[nn:]
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
r.piece = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// next returns the next piece of data to be read.
|
|
||||||
// it returns nil at EOF.
|
|
||||||
func (r *encReader) next() []byte {
|
|
||||||
switch {
|
|
||||||
case r.buf == nil:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case r.piece != nil:
|
|
||||||
// There is still data available for reading.
|
|
||||||
return r.piece
|
|
||||||
|
|
||||||
case r.lhpos < len(r.buf.lheads):
|
|
||||||
// We're before the last list header.
|
|
||||||
head := r.buf.lheads[r.lhpos]
|
|
||||||
sizebefore := head.offset - r.strpos
|
|
||||||
if sizebefore > 0 {
|
|
||||||
// String data before header.
|
|
||||||
p := r.buf.str[r.strpos:head.offset]
|
|
||||||
r.strpos += sizebefore
|
|
||||||
return p
|
|
||||||
} else {
|
|
||||||
r.lhpos++
|
|
||||||
return head.encode(r.buf.sizebuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
case r.strpos < len(r.buf.str):
|
|
||||||
// String data at the end, after all list headers.
|
|
||||||
p := r.buf.str[r.strpos:]
|
|
||||||
r.strpos = len(r.buf.str)
|
|
||||||
return p
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
encoderInterface = reflect.TypeOf(new(Encoder)).Elem()
|
|
||||||
big0 = big.NewInt(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
// makeWriter creates a writer function for the given type.
|
// makeWriter creates a writer function for the given type.
|
||||||
func makeWriter(typ reflect.Type, ts tags) (writer, error) {
|
func makeWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) {
|
||||||
kind := typ.Kind()
|
kind := typ.Kind()
|
||||||
switch {
|
switch {
|
||||||
case typ == rawValueType:
|
case typ == rawValueType:
|
||||||
return writeRawValue, nil
|
return writeRawValue, nil
|
||||||
case typ.Implements(encoderInterface):
|
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||||
return writeEncoder, nil
|
|
||||||
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(encoderInterface):
|
|
||||||
return writeEncoderNoPtr, nil
|
|
||||||
case kind == reflect.Interface:
|
|
||||||
return writeInterface, nil
|
|
||||||
case typ.AssignableTo(reflect.PtrTo(bigInt)):
|
|
||||||
return writeBigIntPtr, nil
|
return writeBigIntPtr, nil
|
||||||
case typ.AssignableTo(bigInt):
|
case typ.AssignableTo(bigInt):
|
||||||
return writeBigIntNoPtr, nil
|
return writeBigIntNoPtr, nil
|
||||||
|
case typ == reflect.PointerTo(u256Int):
|
||||||
|
return writeU256IntPtr, nil
|
||||||
|
case typ == u256Int:
|
||||||
|
return writeU256IntNoPtr, nil
|
||||||
|
case kind == reflect.Ptr:
|
||||||
|
return makePtrWriter(typ, ts)
|
||||||
|
case reflect.PointerTo(typ).Implements(encoderInterface):
|
||||||
|
return makeEncoderWriter(typ), nil
|
||||||
case isUint(kind):
|
case isUint(kind):
|
||||||
return writeUint, nil
|
return writeUint, nil
|
||||||
case kind == reflect.Bool:
|
case kind == reflect.Bool:
|
||||||
|
|
@ -368,97 +162,116 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) {
|
||||||
case kind == reflect.Slice && isByte(typ.Elem()):
|
case kind == reflect.Slice && isByte(typ.Elem()):
|
||||||
return writeBytes, nil
|
return writeBytes, nil
|
||||||
case kind == reflect.Array && isByte(typ.Elem()):
|
case kind == reflect.Array && isByte(typ.Elem()):
|
||||||
return writeByteArray, nil
|
return makeByteArrayWriter(typ), nil
|
||||||
case kind == reflect.Slice || kind == reflect.Array:
|
case kind == reflect.Slice || kind == reflect.Array:
|
||||||
return makeSliceWriter(typ, ts)
|
return makeSliceWriter(typ, ts)
|
||||||
case kind == reflect.Struct:
|
case kind == reflect.Struct:
|
||||||
return makeStructWriter(typ)
|
return makeStructWriter(typ)
|
||||||
case kind == reflect.Ptr:
|
case kind == reflect.Interface:
|
||||||
return makePtrWriter(typ)
|
return writeInterface, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
|
return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isByte(typ reflect.Type) bool {
|
func writeRawValue(val reflect.Value, w *encBuffer) error {
|
||||||
return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeRawValue(val reflect.Value, w *encbuf) error {
|
|
||||||
w.str = append(w.str, val.Bytes()...)
|
w.str = append(w.str, val.Bytes()...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeUint(val reflect.Value, w *encbuf) error {
|
func writeUint(val reflect.Value, w *encBuffer) error {
|
||||||
i := val.Uint()
|
w.writeUint64(val.Uint())
|
||||||
if i == 0 {
|
|
||||||
w.str = append(w.str, 0x80)
|
|
||||||
} else if i < 128 {
|
|
||||||
// fits single byte
|
|
||||||
w.str = append(w.str, byte(i))
|
|
||||||
} else {
|
|
||||||
// TODO: encode int to w.str directly
|
|
||||||
s := putint(w.sizebuf[1:], i)
|
|
||||||
w.sizebuf[0] = 0x80 + byte(s)
|
|
||||||
w.str = append(w.str, w.sizebuf[:s+1]...)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBool(val reflect.Value, w *encbuf) error {
|
func writeBool(val reflect.Value, w *encBuffer) error {
|
||||||
if val.Bool() {
|
w.writeBool(val.Bool())
|
||||||
w.str = append(w.str, 0x01)
|
|
||||||
} else {
|
|
||||||
w.str = append(w.str, 0x80)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBigIntPtr(val reflect.Value, w *encbuf) error {
|
func writeBigIntPtr(val reflect.Value, w *encBuffer) error {
|
||||||
ptr := val.Interface().(*big.Int)
|
ptr := val.Interface().(*big.Int)
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
w.str = append(w.str, 0x80)
|
w.str = append(w.str, 0x80)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return writeBigInt(ptr, w)
|
if ptr.Sign() == -1 {
|
||||||
|
return ErrNegativeBigInt
|
||||||
|
}
|
||||||
|
w.writeBigInt(ptr)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBigIntNoPtr(val reflect.Value, w *encbuf) error {
|
func writeBigIntNoPtr(val reflect.Value, w *encBuffer) error {
|
||||||
i := val.Interface().(big.Int)
|
i := val.Interface().(big.Int)
|
||||||
return writeBigInt(&i, w)
|
if i.Sign() == -1 {
|
||||||
|
return ErrNegativeBigInt
|
||||||
|
}
|
||||||
|
w.writeBigInt(&i)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBigInt(i *big.Int, w *encbuf) error {
|
func writeU256IntPtr(val reflect.Value, w *encBuffer) error {
|
||||||
if cmp := i.Cmp(big0); cmp == -1 {
|
ptr := val.Interface().(*uint256.Int)
|
||||||
return fmt.Errorf("rlp: cannot encode negative *big.Int")
|
if ptr == nil {
|
||||||
} else if cmp == 0 {
|
|
||||||
w.str = append(w.str, 0x80)
|
w.str = append(w.str, 0x80)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.writeUint256(ptr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeU256IntNoPtr(val reflect.Value, w *encBuffer) error {
|
||||||
|
i := val.Interface().(uint256.Int)
|
||||||
|
w.writeUint256(&i)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBytes(val reflect.Value, w *encBuffer) error {
|
||||||
|
w.writeBytes(val.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeByteArrayWriter(typ reflect.Type) writer {
|
||||||
|
switch typ.Len() {
|
||||||
|
case 0:
|
||||||
|
return writeLengthZeroByteArray
|
||||||
|
case 1:
|
||||||
|
return writeLengthOneByteArray
|
||||||
|
default:
|
||||||
|
length := typ.Len()
|
||||||
|
return func(val reflect.Value, w *encBuffer) error {
|
||||||
|
if !val.CanAddr() {
|
||||||
|
// Getting the byte slice of val requires it to be addressable. Make it
|
||||||
|
// addressable by copying.
|
||||||
|
copy := reflect.New(val.Type()).Elem()
|
||||||
|
copy.Set(val)
|
||||||
|
val = copy
|
||||||
|
}
|
||||||
|
slice := byteArrayBytes(val, length)
|
||||||
|
w.encodeStringHeader(len(slice))
|
||||||
|
w.str = append(w.str, slice...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLengthZeroByteArray(val reflect.Value, w *encBuffer) error {
|
||||||
|
w.str = append(w.str, 0x80)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLengthOneByteArray(val reflect.Value, w *encBuffer) error {
|
||||||
|
b := byte(val.Index(0).Uint())
|
||||||
|
if b <= 0x7f {
|
||||||
|
w.str = append(w.str, b)
|
||||||
} else {
|
} else {
|
||||||
w.encodeString(i.Bytes())
|
w.str = append(w.str, 0x81, b)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBytes(val reflect.Value, w *encbuf) error {
|
func writeString(val reflect.Value, w *encBuffer) error {
|
||||||
w.encodeString(val.Bytes())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeByteArray(val reflect.Value, w *encbuf) error {
|
|
||||||
if !val.CanAddr() {
|
|
||||||
// Slice requires the value to be addressable.
|
|
||||||
// Make it addressable by copying.
|
|
||||||
copy := reflect.New(val.Type()).Elem()
|
|
||||||
copy.Set(val)
|
|
||||||
val = copy
|
|
||||||
}
|
|
||||||
size := val.Len()
|
|
||||||
slice := val.Slice(0, size).Bytes()
|
|
||||||
w.encodeString(slice)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeString(val reflect.Value, w *encbuf) error {
|
|
||||||
s := val.String()
|
s := val.String()
|
||||||
if len(s) == 1 && s[0] <= 0x7f {
|
if len(s) == 1 && s[0] <= 0x7f {
|
||||||
// fits single byte, no string header
|
// fits single byte, no string header
|
||||||
|
|
@ -470,27 +283,7 @@ func writeString(val reflect.Value, w *encbuf) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeEncoder(val reflect.Value, w *encbuf) error {
|
func writeInterface(val reflect.Value, w *encBuffer) error {
|
||||||
return val.Interface().(Encoder).EncodeRLP(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeEncoderNoPtr handles non-pointer values that implement Encoder
|
|
||||||
// with a pointer receiver.
|
|
||||||
func writeEncoderNoPtr(val reflect.Value, w *encbuf) error {
|
|
||||||
if !val.CanAddr() {
|
|
||||||
// We can't get the address. It would be possible to make the
|
|
||||||
// value addressable by creating a shallow copy, but this
|
|
||||||
// creates other problems so we're not doing it (yet).
|
|
||||||
//
|
|
||||||
// package json simply doesn't call MarshalJSON for cases like
|
|
||||||
// this, but encodes the value as if it didn't implement the
|
|
||||||
// interface. We don't want to handle it that way.
|
|
||||||
return fmt.Errorf("rlp: game over: unadressable value of type %v, EncodeRLP is pointer method", val.Type())
|
|
||||||
}
|
|
||||||
return val.Addr().Interface().(Encoder).EncodeRLP(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeInterface(val reflect.Value, w *encbuf) error {
|
|
||||||
if val.IsNil() {
|
if val.IsNil() {
|
||||||
// Write empty list. This is consistent with the previous RLP
|
// Write empty list. This is consistent with the previous RLP
|
||||||
// encoder that we had and should therefore avoid any
|
// encoder that we had and should therefore avoid any
|
||||||
|
|
@ -499,31 +292,51 @@ func writeInterface(val reflect.Value, w *encbuf) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
eval := val.Elem()
|
eval := val.Elem()
|
||||||
ti, err := cachedTypeInfo(eval.Type(), tags{})
|
writer, err := cachedWriter(eval.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ti.writer(eval, w)
|
return writer(eval, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeSliceWriter(typ reflect.Type, ts tags) (writer, error) {
|
func makeSliceWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) {
|
||||||
etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{})
|
etypeinfo := theTC.infoWhileGenerating(typ.Elem(), rlpstruct.Tags{})
|
||||||
if err != nil {
|
if etypeinfo.writerErr != nil {
|
||||||
return nil, err
|
return nil, etypeinfo.writerErr
|
||||||
}
|
}
|
||||||
writer := func(val reflect.Value, w *encbuf) error {
|
|
||||||
if !ts.tail {
|
var wfn writer
|
||||||
defer w.listEnd(w.list())
|
if ts.Tail {
|
||||||
}
|
// This is for struct tail slices.
|
||||||
vlen := val.Len()
|
// w.list is not called for them.
|
||||||
for i := 0; i < vlen; i++ {
|
wfn = func(val reflect.Value, w *encBuffer) error {
|
||||||
if err := etypeinfo.writer(val.Index(i), w); err != nil {
|
vlen := val.Len()
|
||||||
return err
|
for i := 0; i < vlen; i++ {
|
||||||
|
if err := etypeinfo.writer(val.Index(i), w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is for regular slices and arrays.
|
||||||
|
wfn = func(val reflect.Value, w *encBuffer) error {
|
||||||
|
vlen := val.Len()
|
||||||
|
if vlen == 0 {
|
||||||
|
w.str = append(w.str, 0xC0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
listOffset := w.list()
|
||||||
|
for i := 0; i < vlen; i++ {
|
||||||
|
if err := etypeinfo.writer(val.Index(i), w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.listEnd(listOffset)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return writer, nil
|
return wfn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeStructWriter(typ reflect.Type) (writer, error) {
|
func makeStructWriter(typ reflect.Type) (writer, error) {
|
||||||
|
|
@ -531,56 +344,86 @@ func makeStructWriter(typ reflect.Type) (writer, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
writer := func(val reflect.Value, w *encbuf) error {
|
for _, f := range fields {
|
||||||
lh := w.list()
|
if f.info.writerErr != nil {
|
||||||
for _, f := range fields {
|
return nil, structFieldError{typ, f.index, f.info.writerErr}
|
||||||
if err := f.info.writer(val.Field(f.index), w); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.listEnd(lh)
|
}
|
||||||
|
|
||||||
|
var writer writer
|
||||||
|
firstOptionalField := firstOptionalField(fields)
|
||||||
|
if firstOptionalField == len(fields) {
|
||||||
|
// This is the writer function for structs without any optional fields.
|
||||||
|
writer = func(val reflect.Value, w *encBuffer) error {
|
||||||
|
lh := w.list()
|
||||||
|
for _, f := range fields {
|
||||||
|
if err := f.info.writer(val.Field(f.index), w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.listEnd(lh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If there are any "optional" fields, the writer needs to perform additional
|
||||||
|
// checks to determine the output list length.
|
||||||
|
writer = func(val reflect.Value, w *encBuffer) error {
|
||||||
|
lastField := len(fields) - 1
|
||||||
|
for ; lastField >= firstOptionalField; lastField-- {
|
||||||
|
if !val.Field(fields[lastField].index).IsZero() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lh := w.list()
|
||||||
|
for i := 0; i <= lastField; i++ {
|
||||||
|
if err := fields[i].info.writer(val.Field(fields[i].index), w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.listEnd(lh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePtrWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) {
|
||||||
|
nilEncoding := byte(0xC0)
|
||||||
|
if typeNilKind(typ.Elem(), ts) == String {
|
||||||
|
nilEncoding = 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
etypeinfo := theTC.infoWhileGenerating(typ.Elem(), rlpstruct.Tags{})
|
||||||
|
if etypeinfo.writerErr != nil {
|
||||||
|
return nil, etypeinfo.writerErr
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := func(val reflect.Value, w *encBuffer) error {
|
||||||
|
if ev := val.Elem(); ev.IsValid() {
|
||||||
|
return etypeinfo.writer(ev, w)
|
||||||
|
}
|
||||||
|
w.str = append(w.str, nilEncoding)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return writer, nil
|
return writer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePtrWriter(typ reflect.Type) (writer, error) {
|
func makeEncoderWriter(typ reflect.Type) writer {
|
||||||
etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{})
|
if typ.Implements(encoderInterface) {
|
||||||
if err != nil {
|
return func(val reflect.Value, w *encBuffer) error {
|
||||||
return nil, err
|
return val.Interface().(Encoder).EncodeRLP(w)
|
||||||
}
|
|
||||||
|
|
||||||
// determine nil pointer handler
|
|
||||||
var nilfunc func(*encbuf) error
|
|
||||||
kind := typ.Elem().Kind()
|
|
||||||
switch {
|
|
||||||
case kind == reflect.Array && isByte(typ.Elem().Elem()):
|
|
||||||
nilfunc = func(w *encbuf) error {
|
|
||||||
w.str = append(w.str, 0x80)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case kind == reflect.Struct || kind == reflect.Array:
|
|
||||||
nilfunc = func(w *encbuf) error {
|
|
||||||
// encoding the zero value of a struct/array could trigger
|
|
||||||
// infinite recursion, avoid that.
|
|
||||||
w.listEnd(w.list())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
zero := reflect.Zero(typ.Elem())
|
|
||||||
nilfunc = func(w *encbuf) error {
|
|
||||||
return etypeinfo.writer(zero, w)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
w := func(val reflect.Value, w *encBuffer) error {
|
||||||
writer := func(val reflect.Value, w *encbuf) error {
|
if !val.CanAddr() {
|
||||||
if val.IsNil() {
|
// package json simply doesn't call MarshalJSON for this case, but encodes the
|
||||||
return nilfunc(w)
|
// value as if it didn't implement the interface. We don't want to handle it that
|
||||||
} else {
|
// way.
|
||||||
return etypeinfo.writer(val.Elem(), w)
|
return fmt.Errorf("rlp: unaddressable value of type %v, EncodeRLP is pointer method", val.Type())
|
||||||
}
|
}
|
||||||
|
return val.Addr().Interface().(Encoder).EncodeRLP(w)
|
||||||
}
|
}
|
||||||
return writer, err
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
// putint writes i to the beginning of b in big endian byte
|
// putint writes i to the beginning of b in big endian byte
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testEncoder struct {
|
type testEncoder struct {
|
||||||
|
|
@ -32,12 +36,19 @@ type testEncoder struct {
|
||||||
|
|
||||||
func (e *testEncoder) EncodeRLP(w io.Writer) error {
|
func (e *testEncoder) EncodeRLP(w io.Writer) error {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
w.Write([]byte{0, 0, 0, 0})
|
panic("EncodeRLP called on nil value")
|
||||||
} else if e.err != nil {
|
|
||||||
return e.err
|
|
||||||
} else {
|
|
||||||
w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1})
|
|
||||||
}
|
}
|
||||||
|
if e.err != nil {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type testEncoderValueMethod struct{}
|
||||||
|
|
||||||
|
func (e testEncoderValueMethod) EncodeRLP(w io.Writer) error {
|
||||||
|
w.Write([]byte{0xFA, 0xFE, 0xF0})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +59,13 @@ func (e byteEncoder) EncodeRLP(w io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type undecodableEncoder func()
|
||||||
|
|
||||||
|
func (f undecodableEncoder) EncodeRLP(w io.Writer) error {
|
||||||
|
w.Write([]byte{0xF5, 0xF5, 0xF5})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type encodableReader struct {
|
type encodableReader struct {
|
||||||
A, B uint
|
A, B uint
|
||||||
}
|
}
|
||||||
|
|
@ -102,35 +120,95 @@ var encTests = []encTest{
|
||||||
{val: big.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"},
|
{val: big.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"},
|
||||||
{val: big.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"},
|
{val: big.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"},
|
||||||
{
|
{
|
||||||
val: big.NewInt(0).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")),
|
val: new(big.Int).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")),
|
||||||
output: "8F102030405060708090A0B0C0D0E0F2",
|
output: "8F102030405060708090A0B0C0D0E0F2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
val: big.NewInt(0).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")),
|
val: new(big.Int).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")),
|
||||||
output: "9C0100020003000400050006000700080009000A000B000C000D000E01",
|
output: "9C0100020003000400050006000700080009000A000B000C000D000E01",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
val: big.NewInt(0).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")),
|
val: new(big.Int).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")),
|
||||||
output: "A1010000000000000000000000000000000000000000000000000000000000000000",
|
output: "A1010000000000000000000000000000000000000000000000000000000000000000",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
val: veryBigInt,
|
||||||
|
output: "89FFFFFFFFFFFFFFFFFF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: veryVeryBigInt,
|
||||||
|
output: "B848FFFFFFFFFFFFFFFFF800000000000000001BFFFFFFFFFFFFFFFFC8000000000000000045FFFFFFFFFFFFFFFFC800000000000000001BFFFFFFFFFFFFFFFFF8000000000000000001",
|
||||||
|
},
|
||||||
|
|
||||||
// non-pointer big.Int
|
// non-pointer big.Int
|
||||||
{val: *big.NewInt(0), output: "80"},
|
{val: *big.NewInt(0), output: "80"},
|
||||||
{val: *big.NewInt(0xFFFFFF), output: "83FFFFFF"},
|
{val: *big.NewInt(0xFFFFFF), output: "83FFFFFF"},
|
||||||
|
|
||||||
// negative ints are not supported
|
// negative ints are not supported
|
||||||
{val: big.NewInt(-1), error: "rlp: cannot encode negative *big.Int"},
|
{val: big.NewInt(-1), error: "rlp: cannot encode negative big.Int"},
|
||||||
|
{val: *big.NewInt(-1), error: "rlp: cannot encode negative big.Int"},
|
||||||
|
|
||||||
// byte slices, strings
|
// uint256
|
||||||
|
{val: uint256.NewInt(0), output: "80"},
|
||||||
|
{val: uint256.NewInt(1), output: "01"},
|
||||||
|
{val: uint256.NewInt(127), output: "7F"},
|
||||||
|
{val: uint256.NewInt(128), output: "8180"},
|
||||||
|
{val: uint256.NewInt(256), output: "820100"},
|
||||||
|
{val: uint256.NewInt(1024), output: "820400"},
|
||||||
|
{val: uint256.NewInt(0xFFFFFF), output: "83FFFFFF"},
|
||||||
|
{val: uint256.NewInt(0xFFFFFFFF), output: "84FFFFFFFF"},
|
||||||
|
{val: uint256.NewInt(0xFFFFFFFFFF), output: "85FFFFFFFFFF"},
|
||||||
|
{val: uint256.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"},
|
||||||
|
{val: uint256.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"},
|
||||||
|
{
|
||||||
|
val: new(uint256.Int).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")),
|
||||||
|
output: "8F102030405060708090A0B0C0D0E0F2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: new(uint256.Int).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")),
|
||||||
|
output: "9C0100020003000400050006000700080009000A000B000C000D000E01",
|
||||||
|
},
|
||||||
|
// non-pointer uint256.Int
|
||||||
|
{val: *uint256.NewInt(0), output: "80"},
|
||||||
|
{val: *uint256.NewInt(0xFFFFFF), output: "83FFFFFF"},
|
||||||
|
|
||||||
|
// byte arrays
|
||||||
|
{val: [0]byte{}, output: "80"},
|
||||||
|
{val: [1]byte{0}, output: "00"},
|
||||||
|
{val: [1]byte{1}, output: "01"},
|
||||||
|
{val: [1]byte{0x7F}, output: "7F"},
|
||||||
|
{val: [1]byte{0x80}, output: "8180"},
|
||||||
|
{val: [1]byte{0xFF}, output: "81FF"},
|
||||||
|
{val: [3]byte{1, 2, 3}, output: "83010203"},
|
||||||
|
{val: [57]byte{1, 2, 3}, output: "B839010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
|
||||||
|
|
||||||
|
// named byte type arrays
|
||||||
|
{val: [0]namedByteType{}, output: "80"},
|
||||||
|
{val: [1]namedByteType{0}, output: "00"},
|
||||||
|
{val: [1]namedByteType{1}, output: "01"},
|
||||||
|
{val: [1]namedByteType{0x7F}, output: "7F"},
|
||||||
|
{val: [1]namedByteType{0x80}, output: "8180"},
|
||||||
|
{val: [1]namedByteType{0xFF}, output: "81FF"},
|
||||||
|
{val: [3]namedByteType{1, 2, 3}, output: "83010203"},
|
||||||
|
{val: [57]namedByteType{1, 2, 3}, output: "B839010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
|
||||||
|
|
||||||
|
// byte slices
|
||||||
{val: []byte{}, output: "80"},
|
{val: []byte{}, output: "80"},
|
||||||
|
{val: []byte{0}, output: "00"},
|
||||||
{val: []byte{0x7E}, output: "7E"},
|
{val: []byte{0x7E}, output: "7E"},
|
||||||
{val: []byte{0x7F}, output: "7F"},
|
{val: []byte{0x7F}, output: "7F"},
|
||||||
{val: []byte{0x80}, output: "8180"},
|
{val: []byte{0x80}, output: "8180"},
|
||||||
{val: []byte{1, 2, 3}, output: "83010203"},
|
{val: []byte{1, 2, 3}, output: "83010203"},
|
||||||
|
|
||||||
|
// named byte type slices
|
||||||
|
{val: []namedByteType{}, output: "80"},
|
||||||
|
{val: []namedByteType{0}, output: "00"},
|
||||||
|
{val: []namedByteType{0x7E}, output: "7E"},
|
||||||
|
{val: []namedByteType{0x7F}, output: "7F"},
|
||||||
|
{val: []namedByteType{0x80}, output: "8180"},
|
||||||
{val: []namedByteType{1, 2, 3}, output: "83010203"},
|
{val: []namedByteType{1, 2, 3}, output: "83010203"},
|
||||||
{val: [...]namedByteType{1, 2, 3}, output: "83010203"},
|
|
||||||
|
|
||||||
|
// strings
|
||||||
{val: "", output: "80"},
|
{val: "", output: "80"},
|
||||||
{val: "\x7E", output: "7E"},
|
{val: "\x7E", output: "7E"},
|
||||||
{val: "\x7F", output: "7F"},
|
{val: "\x7F", output: "7F"},
|
||||||
|
|
@ -203,6 +281,12 @@ var encTests = []encTest{
|
||||||
output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376",
|
output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Non-byte arrays are encoded as lists.
|
||||||
|
// Note that it is important to test [4]uint64 specifically,
|
||||||
|
// because that's the underlying type of uint256.Int.
|
||||||
|
{val: [4]uint32{1, 2, 3, 4}, output: "C401020304"},
|
||||||
|
{val: [4]uint64{1, 2, 3, 4}, output: "C401020304"},
|
||||||
|
|
||||||
// RawValue
|
// RawValue
|
||||||
{val: RawValue(unhex("01")), output: "01"},
|
{val: RawValue(unhex("01")), output: "01"},
|
||||||
{val: RawValue(unhex("82FFFF")), output: "82FFFF"},
|
{val: RawValue(unhex("82FFFF")), output: "82FFFF"},
|
||||||
|
|
@ -213,11 +297,34 @@ var encTests = []encTest{
|
||||||
{val: simplestruct{A: 3, B: "foo"}, output: "C50383666F6F"},
|
{val: simplestruct{A: 3, B: "foo"}, output: "C50383666F6F"},
|
||||||
{val: &recstruct{5, nil}, output: "C205C0"},
|
{val: &recstruct{5, nil}, output: "C205C0"},
|
||||||
{val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"},
|
{val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"},
|
||||||
|
{val: &intField{X: 3}, error: "rlp: type int is not RLP-serializable (struct field rlp.intField.X)"},
|
||||||
|
|
||||||
|
// struct tag "-"
|
||||||
|
{val: &ignoredField{A: 1, B: 2, C: 3}, output: "C20103"},
|
||||||
|
|
||||||
|
// struct tag "tail"
|
||||||
{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02"), unhex("03")}}, output: "C3010203"},
|
{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02"), unhex("03")}}, output: "C3010203"},
|
||||||
{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"},
|
{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"},
|
||||||
{val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"},
|
{val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"},
|
||||||
{val: &tailRaw{A: 1, Tail: nil}, output: "C101"},
|
{val: &tailRaw{A: 1, Tail: nil}, output: "C101"},
|
||||||
{val: &hasIgnoredField{A: 1, B: 2, C: 3}, output: "C20103"},
|
|
||||||
|
// struct tag "optional"
|
||||||
|
{val: &optionalFields{}, output: "C180"},
|
||||||
|
{val: &optionalFields{A: 1}, output: "C101"},
|
||||||
|
{val: &optionalFields{A: 1, B: 2}, output: "C20102"},
|
||||||
|
{val: &optionalFields{A: 1, B: 2, C: 3}, output: "C3010203"},
|
||||||
|
{val: &optionalFields{A: 1, B: 0, C: 3}, output: "C3018003"},
|
||||||
|
{val: &optionalAndTailField{A: 1}, output: "C101"},
|
||||||
|
{val: &optionalAndTailField{A: 1, B: 2}, output: "C20102"},
|
||||||
|
{val: &optionalAndTailField{A: 1, Tail: []uint{5, 6}}, output: "C401800506"},
|
||||||
|
{val: &optionalAndTailField{A: 1, Tail: []uint{5, 6}}, output: "C401800506"},
|
||||||
|
{val: &optionalBigIntField{A: 1}, output: "C101"},
|
||||||
|
{val: &optionalPtrField{A: 1}, output: "C101"},
|
||||||
|
{val: &optionalPtrFieldNil{A: 1}, output: "C101"},
|
||||||
|
{val: &multipleOptionalFields{A: nil, B: nil}, output: "C0"},
|
||||||
|
{val: &multipleOptionalFields{A: &[3]byte{1, 2, 3}, B: &[3]byte{1, 2, 3}}, output: "C88301020383010203"},
|
||||||
|
{val: &multipleOptionalFields{A: nil, B: &[3]byte{1, 2, 3}}, output: "C58083010203"}, // encodes without error but decode will fail
|
||||||
|
{val: &nonOptionalPtrField{A: 1}, output: "C20180"}, // encodes without error but decode will fail
|
||||||
|
|
||||||
// nil
|
// nil
|
||||||
{val: (*uint)(nil), output: "80"},
|
{val: (*uint)(nil), output: "80"},
|
||||||
|
|
@ -225,26 +332,73 @@ var encTests = []encTest{
|
||||||
{val: (*[]byte)(nil), output: "80"},
|
{val: (*[]byte)(nil), output: "80"},
|
||||||
{val: (*[10]byte)(nil), output: "80"},
|
{val: (*[10]byte)(nil), output: "80"},
|
||||||
{val: (*big.Int)(nil), output: "80"},
|
{val: (*big.Int)(nil), output: "80"},
|
||||||
|
{val: (*uint256.Int)(nil), output: "80"},
|
||||||
{val: (*[]string)(nil), output: "C0"},
|
{val: (*[]string)(nil), output: "C0"},
|
||||||
{val: (*[10]string)(nil), output: "C0"},
|
{val: (*[10]string)(nil), output: "C0"},
|
||||||
{val: (*[]interface{})(nil), output: "C0"},
|
{val: (*[]interface{})(nil), output: "C0"},
|
||||||
{val: (*[]struct{ uint })(nil), output: "C0"},
|
{val: (*[]struct{ uint })(nil), output: "C0"},
|
||||||
{val: (*interface{})(nil), output: "C0"},
|
{val: (*interface{})(nil), output: "C0"},
|
||||||
|
|
||||||
|
// nil struct fields
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *[]byte
|
||||||
|
}{},
|
||||||
|
output: "C180",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *[2]byte
|
||||||
|
}{},
|
||||||
|
output: "C180",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *uint64
|
||||||
|
}{},
|
||||||
|
output: "C180",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *uint64 `rlp:"nilList"`
|
||||||
|
}{},
|
||||||
|
output: "C1C0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *[]uint64
|
||||||
|
}{},
|
||||||
|
output: "C1C0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: struct {
|
||||||
|
X *[]uint64 `rlp:"nilString"`
|
||||||
|
}{},
|
||||||
|
output: "C180",
|
||||||
|
},
|
||||||
|
|
||||||
// interfaces
|
// interfaces
|
||||||
{val: []io.Reader{reader}, output: "C3C20102"}, // the contained value is a struct
|
{val: []io.Reader{reader}, output: "C3C20102"}, // the contained value is a struct
|
||||||
|
|
||||||
// Encoder
|
// Encoder
|
||||||
{val: (*testEncoder)(nil), output: "00000000"},
|
{val: (*testEncoder)(nil), output: "C0"},
|
||||||
{val: &testEncoder{}, output: "00010001000100010001"},
|
{val: &testEncoder{}, output: "00010001000100010001"},
|
||||||
{val: &testEncoder{errors.New("test error")}, error: "test error"},
|
{val: &testEncoder{errors.New("test error")}, error: "test error"},
|
||||||
// verify that pointer method testEncoder.EncodeRLP is called for
|
{val: struct{ E testEncoderValueMethod }{}, output: "C3FAFEF0"},
|
||||||
|
{val: struct{ E *testEncoderValueMethod }{}, output: "C1C0"},
|
||||||
|
|
||||||
|
// Verify that the Encoder interface works for unsupported types like func().
|
||||||
|
{val: undecodableEncoder(func() {}), output: "F5F5F5"},
|
||||||
|
|
||||||
|
// Verify that pointer method testEncoder.EncodeRLP is called for
|
||||||
// addressable non-pointer values.
|
// addressable non-pointer values.
|
||||||
{val: &struct{ TE testEncoder }{testEncoder{}}, output: "CA00010001000100010001"},
|
{val: &struct{ TE testEncoder }{testEncoder{}}, output: "CA00010001000100010001"},
|
||||||
{val: &struct{ TE testEncoder }{testEncoder{errors.New("test error")}}, error: "test error"},
|
{val: &struct{ TE testEncoder }{testEncoder{errors.New("test error")}}, error: "test error"},
|
||||||
// verify the error for non-addressable non-pointer Encoder
|
|
||||||
{val: testEncoder{}, error: "rlp: game over: unadressable value of type rlp.testEncoder, EncodeRLP is pointer method"},
|
// Verify the error for non-addressable non-pointer Encoder.
|
||||||
// verify the special case for []byte
|
{val: testEncoder{}, error: "rlp: unaddressable value of type rlp.testEncoder, EncodeRLP is pointer method"},
|
||||||
|
|
||||||
|
// Verify Encoder takes precedence over []byte.
|
||||||
{val: []byteEncoder{0, 1, 2, 3, 4}, output: "C5C0C0C0C0C0"},
|
{val: []byteEncoder{0, 1, 2, 3, 4}, output: "C5C0C0C0C0C0"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,6 +434,21 @@ func TestEncodeToBytes(t *testing.T) {
|
||||||
runEncTests(t, EncodeToBytes)
|
runEncTests(t, EncodeToBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEncodeAppendToBytes(t *testing.T) {
|
||||||
|
buffer := make([]byte, 20)
|
||||||
|
runEncTests(t, func(val interface{}) ([]byte, error) {
|
||||||
|
w := NewEncoderBuffer(nil)
|
||||||
|
defer w.Flush()
|
||||||
|
|
||||||
|
err := Encode(w, val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
output := w.AppendToBytes(buffer[:0])
|
||||||
|
return output, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestEncodeToReader(t *testing.T) {
|
func TestEncodeToReader(t *testing.T) {
|
||||||
runEncTests(t, func(val interface{}) ([]byte, error) {
|
runEncTests(t, func(val interface{}) ([]byte, error) {
|
||||||
_, r, err := EncodeToReader(val)
|
_, r, err := EncodeToReader(val)
|
||||||
|
|
@ -338,3 +507,132 @@ func TestEncodeToReaderReturnToPool(t *testing.T) {
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sink interface{}
|
||||||
|
|
||||||
|
func BenchmarkIntsize(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
sink = intsize(0x12345678)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPutint(b *testing.B) {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
putint(buf, 0x12345678)
|
||||||
|
sink = buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeBigInts(b *testing.B) {
|
||||||
|
ints := make([]*big.Int, 200)
|
||||||
|
for i := range ints {
|
||||||
|
ints[i] = math.BigPow(2, int64(i))
|
||||||
|
}
|
||||||
|
out := bytes.NewBuffer(make([]byte, 0, 4096))
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out.Reset()
|
||||||
|
if err := Encode(out, ints); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeU256Ints(b *testing.B) {
|
||||||
|
ints := make([]*uint256.Int, 200)
|
||||||
|
for i := range ints {
|
||||||
|
ints[i], _ = uint256.FromBig(math.BigPow(2, int64(i)))
|
||||||
|
}
|
||||||
|
out := bytes.NewBuffer(make([]byte, 0, 4096))
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out.Reset()
|
||||||
|
if err := Encode(out, ints); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeConcurrentInterface(b *testing.B) {
|
||||||
|
type struct1 struct {
|
||||||
|
A string
|
||||||
|
B *big.Int
|
||||||
|
C [20]byte
|
||||||
|
}
|
||||||
|
value := []interface{}{
|
||||||
|
uint(999),
|
||||||
|
&struct1{A: "hello", B: big.NewInt(0xFFFFFFFF)},
|
||||||
|
[10]byte{1, 2, 3, 4, 5, 6},
|
||||||
|
[]string{"yeah", "yeah", "yeah"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for cpu := 0; cpu < runtime.NumCPU(); cpu++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
buffer.Reset()
|
||||||
|
err := Encode(&buffer, value)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
type byteArrayStruct struct {
|
||||||
|
A [20]byte
|
||||||
|
B [32]byte
|
||||||
|
C [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeByteArrayStruct(b *testing.B) {
|
||||||
|
var out bytes.Buffer
|
||||||
|
var value byteArrayStruct
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out.Reset()
|
||||||
|
if err := Encode(&out, &value); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type structSliceElem struct {
|
||||||
|
X uint64
|
||||||
|
Y uint64
|
||||||
|
Z uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type structPtrSlice []*structSliceElem
|
||||||
|
|
||||||
|
func BenchmarkEncodeStructPtrSlice(b *testing.B) {
|
||||||
|
var out bytes.Buffer
|
||||||
|
var value = structPtrSlice{
|
||||||
|
&structSliceElem{1, 1, 1},
|
||||||
|
&structSliceElem{2, 2, 2},
|
||||||
|
&structSliceElem{3, 3, 3},
|
||||||
|
&structSliceElem{5, 5, 5},
|
||||||
|
&structSliceElem{6, 6, 6},
|
||||||
|
&structSliceElem{7, 7, 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out.Reset()
|
||||||
|
if err := Encode(&out, &value); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,13 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package rlp
|
package rlp_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MyCoolType struct {
|
type MyCoolType struct {
|
||||||
|
|
@ -28,27 +30,19 @@ type MyCoolType struct {
|
||||||
|
|
||||||
// EncodeRLP writes x as RLP list [a, b] that omits the Name field.
|
// EncodeRLP writes x as RLP list [a, b] that omits the Name field.
|
||||||
func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) {
|
func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) {
|
||||||
// Note: the receiver can be a nil pointer. This allows you to
|
return rlp.Encode(w, []uint{x.a, x.b})
|
||||||
// control the encoding of nil, but it also means that you have to
|
|
||||||
// check for a nil receiver.
|
|
||||||
if x == nil {
|
|
||||||
err = Encode(w, []uint{0, 0})
|
|
||||||
} else {
|
|
||||||
err = Encode(w, []uint{x.a, x.b})
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleEncoder() {
|
func ExampleEncoder() {
|
||||||
var t *MyCoolType // t is nil pointer to MyCoolType
|
var t *MyCoolType // t is nil pointer to MyCoolType
|
||||||
bytes, _ := EncodeToBytes(t)
|
bytes, _ := rlp.EncodeToBytes(t)
|
||||||
fmt.Printf("%v → %X\n", t, bytes)
|
fmt.Printf("%v → %X\n", t, bytes)
|
||||||
|
|
||||||
t = &MyCoolType{Name: "foobar", a: 5, b: 6}
|
t = &MyCoolType{Name: "foobar", a: 5, b: 6}
|
||||||
bytes, _ = EncodeToBytes(t)
|
bytes, _ = rlp.EncodeToBytes(t)
|
||||||
fmt.Printf("%v → %X\n", t, bytes)
|
fmt.Printf("%v → %X\n", t, bytes)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// <nil> → C28080
|
// <nil> → C0
|
||||||
// &{foobar 5 6} → C20506
|
// &{foobar 5 6} → C20506
|
||||||
}
|
}
|
||||||
|
|
|
||||||
213
rlp/internal/rlpstruct/rlpstruct.go
Normal file
213
rlp/internal/rlpstruct/rlpstruct.go
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package rlpstruct implements struct processing for RLP encoding/decoding.
|
||||||
|
//
|
||||||
|
// In particular, this package handles all rules around field filtering,
|
||||||
|
// struct tags and nil value determination.
|
||||||
|
package rlpstruct
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field represents a struct field.
|
||||||
|
type Field struct {
|
||||||
|
Name string
|
||||||
|
Index int
|
||||||
|
Exported bool
|
||||||
|
Type Type
|
||||||
|
Tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type represents the attributes of a Go type.
|
||||||
|
type Type struct {
|
||||||
|
Name string
|
||||||
|
Kind reflect.Kind
|
||||||
|
IsEncoder bool // whether type implements rlp.Encoder
|
||||||
|
IsDecoder bool // whether type implements rlp.Decoder
|
||||||
|
Elem *Type // non-nil for Kind values of Ptr, Slice, Array
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultNilValue determines whether a nil pointer to t encodes/decodes
|
||||||
|
// as an empty string or empty list.
|
||||||
|
func (t Type) DefaultNilValue() NilKind {
|
||||||
|
k := t.Kind
|
||||||
|
if isUint(k) || k == reflect.String || k == reflect.Bool || isByteArray(t) {
|
||||||
|
return NilKindString
|
||||||
|
}
|
||||||
|
return NilKindList
|
||||||
|
}
|
||||||
|
|
||||||
|
// NilKind is the RLP value encoded in place of nil pointers.
|
||||||
|
type NilKind uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
NilKindString NilKind = 0x80
|
||||||
|
NilKindList NilKind = 0xC0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tags represents struct tags.
|
||||||
|
type Tags struct {
|
||||||
|
// rlp:"nil" controls whether empty input results in a nil pointer.
|
||||||
|
// nilKind is the kind of empty value allowed for the field.
|
||||||
|
NilKind NilKind
|
||||||
|
NilOK bool
|
||||||
|
|
||||||
|
// rlp:"optional" allows for a field to be missing in the input list.
|
||||||
|
// If this is set, all subsequent fields must also be optional.
|
||||||
|
Optional bool
|
||||||
|
|
||||||
|
// rlp:"tail" controls whether this field swallows additional list elements. It can
|
||||||
|
// only be set for the last field, which must be of slice type.
|
||||||
|
Tail bool
|
||||||
|
|
||||||
|
// rlp:"-" ignores fields.
|
||||||
|
Ignored bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagError is raised for invalid struct tags.
|
||||||
|
type TagError struct {
|
||||||
|
StructType string
|
||||||
|
|
||||||
|
// These are set by this package.
|
||||||
|
Field string
|
||||||
|
Tag string
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TagError) Error() string {
|
||||||
|
field := "field " + e.Field
|
||||||
|
if e.StructType != "" {
|
||||||
|
field = e.StructType + "." + e.Field
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("rlp: invalid struct tag %q for %s (%s)", e.Tag, field, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessFields filters the given struct fields, returning only fields
|
||||||
|
// that should be considered for encoding/decoding.
|
||||||
|
func ProcessFields(allFields []Field) ([]Field, []Tags, error) {
|
||||||
|
lastPublic := lastPublicField(allFields)
|
||||||
|
|
||||||
|
// Gather all exported fields and their tags.
|
||||||
|
var fields []Field
|
||||||
|
var tags []Tags
|
||||||
|
for _, field := range allFields {
|
||||||
|
if !field.Exported {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ts, err := parseTag(field, lastPublic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if ts.Ignored {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields = append(fields, field)
|
||||||
|
tags = append(tags, ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify optional field consistency. If any optional field exists,
|
||||||
|
// all fields after it must also be optional. Note: optional + tail
|
||||||
|
// is supported.
|
||||||
|
var anyOptional bool
|
||||||
|
var firstOptionalName string
|
||||||
|
for i, ts := range tags {
|
||||||
|
name := fields[i].Name
|
||||||
|
if ts.Optional || ts.Tail {
|
||||||
|
if !anyOptional {
|
||||||
|
firstOptionalName = name
|
||||||
|
}
|
||||||
|
anyOptional = true
|
||||||
|
} else {
|
||||||
|
if anyOptional {
|
||||||
|
msg := fmt.Sprintf("must be optional because preceding field %q is optional", firstOptionalName)
|
||||||
|
return nil, nil, TagError{Field: name, Err: msg}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fields, tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTag(field Field, lastPublic int) (Tags, error) {
|
||||||
|
name := field.Name
|
||||||
|
tag := reflect.StructTag(field.Tag)
|
||||||
|
var ts Tags
|
||||||
|
for _, t := range strings.Split(tag.Get("rlp"), ",") {
|
||||||
|
switch t = strings.TrimSpace(t); t {
|
||||||
|
case "":
|
||||||
|
// empty tag is allowed for some reason
|
||||||
|
case "-":
|
||||||
|
ts.Ignored = true
|
||||||
|
case "nil", "nilString", "nilList":
|
||||||
|
ts.NilOK = true
|
||||||
|
if field.Type.Kind != reflect.Ptr {
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: "field is not a pointer"}
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case "nil":
|
||||||
|
ts.NilKind = field.Type.Elem.DefaultNilValue()
|
||||||
|
case "nilString":
|
||||||
|
ts.NilKind = NilKindString
|
||||||
|
case "nilList":
|
||||||
|
ts.NilKind = NilKindList
|
||||||
|
}
|
||||||
|
case "optional":
|
||||||
|
ts.Optional = true
|
||||||
|
if ts.Tail {
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: `also has "tail" tag`}
|
||||||
|
}
|
||||||
|
case "tail":
|
||||||
|
ts.Tail = true
|
||||||
|
if field.Index != lastPublic {
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: "must be on last field"}
|
||||||
|
}
|
||||||
|
if ts.Optional {
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: `also has "optional" tag`}
|
||||||
|
}
|
||||||
|
if field.Type.Kind != reflect.Slice {
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: "field type is not slice"}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ts, TagError{Field: name, Tag: t, Err: "unknown tag"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lastPublicField(fields []Field) int {
|
||||||
|
last := 0
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Exported {
|
||||||
|
last = f.Index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUint(k reflect.Kind) bool {
|
||||||
|
return k >= reflect.Uint && k <= reflect.Uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func isByte(typ Type) bool {
|
||||||
|
return typ.Kind == reflect.Uint8 && !typ.IsEncoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func isByteArray(typ Type) bool {
|
||||||
|
return (typ.Kind == reflect.Slice || typ.Kind == reflect.Array) && isByte(*typ.Elem)
|
||||||
|
}
|
||||||
59
rlp/iterator.go
Normal file
59
rlp/iterator.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2020 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package rlp
|
||||||
|
|
||||||
|
type listIterator struct {
|
||||||
|
data []byte
|
||||||
|
next []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListIterator creates an iterator for the (list) represented by data
|
||||||
|
func NewListIterator(data RawValue) (*listIterator, error) {
|
||||||
|
k, t, c, err := readKind(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if k != List {
|
||||||
|
return nil, ErrExpectedList
|
||||||
|
}
|
||||||
|
it := &listIterator{
|
||||||
|
data: data[t : t+c],
|
||||||
|
}
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next forwards the iterator one step, returns true if it was not at end yet
|
||||||
|
func (it *listIterator) Next() bool {
|
||||||
|
if len(it.data) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, t, c, err := readKind(it.data)
|
||||||
|
it.next = it.data[:t+c]
|
||||||
|
it.data = it.data[t+c:]
|
||||||
|
it.err = err
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the current value
|
||||||
|
func (it *listIterator) Value() []byte {
|
||||||
|
return it.next
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *listIterator) Err() error {
|
||||||
|
return it.err
|
||||||
|
}
|
||||||
59
rlp/iterator_test.go
Normal file
59
rlp/iterator_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2020 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package rlp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestIterator tests some basic things about the ListIterator. A more
|
||||||
|
// comprehensive test can be found in core/rlp_test.go, where we can
|
||||||
|
// use both types and rlp without dependency cycles
|
||||||
|
func TestIterator(t *testing.T) {
|
||||||
|
bodyRlpHex := "0xf902cbf8d6f869800182c35094000000000000000000000000000000000000aaaa808a000000000000000000001ba01025c66fad28b4ce3370222624d952c35529e602af7cbe04f667371f61b0e3b3a00ab8813514d1217059748fd903288ace1b4001a4bc5fbde2790debdc8167de2ff869010182c35094000000000000000000000000000000000000aaaa808a000000000000000000001ca05ac4cf1d19be06f3742c21df6c49a7e929ceb3dbaf6a09f3cfb56ff6828bd9a7a06875970133a35e63ac06d360aa166d228cc013e9b96e0a2cae7f55b22e1ee2e8f901f0f901eda0c75448377c0e426b8017b23c5f77379ecf69abc1d5c224284ad3ba1c46c59adaa00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
|
||||||
|
bodyRlp := hexutil.MustDecode(bodyRlpHex)
|
||||||
|
|
||||||
|
it, err := NewListIterator(bodyRlp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Check that txs exist
|
||||||
|
if !it.Next() {
|
||||||
|
t.Fatal("expected two elems, got zero")
|
||||||
|
}
|
||||||
|
txs := it.Value()
|
||||||
|
// Check that uncles exist
|
||||||
|
if !it.Next() {
|
||||||
|
t.Fatal("expected two elems, got one")
|
||||||
|
}
|
||||||
|
txit, err := NewListIterator(txs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var i = 0
|
||||||
|
for txit.Next() {
|
||||||
|
if txit.err != nil {
|
||||||
|
t.Fatal(txit.err)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if exp := 2; i != exp {
|
||||||
|
t.Errorf("count wrong, expected %d got %d", i, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
rlp/raw.go
67
rlp/raw.go
|
|
@ -28,12 +28,53 @@ type RawValue []byte
|
||||||
|
|
||||||
var rawValueType = reflect.TypeOf(RawValue{})
|
var rawValueType = reflect.TypeOf(RawValue{})
|
||||||
|
|
||||||
|
// StringSize returns the encoded size of a string.
|
||||||
|
func StringSize(s string) uint64 {
|
||||||
|
switch {
|
||||||
|
case len(s) == 0:
|
||||||
|
return 1
|
||||||
|
case len(s) == 1:
|
||||||
|
if s[0] <= 0x7f {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return uint64(headsize(uint64(len(s))) + len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSize returns the encoded size of a byte slice.
|
||||||
|
func BytesSize(b []byte) uint64 {
|
||||||
|
switch {
|
||||||
|
case len(b) == 0:
|
||||||
|
return 1
|
||||||
|
case len(b) == 1:
|
||||||
|
if b[0] <= 0x7f {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return uint64(headsize(uint64(len(b))) + len(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ListSize returns the encoded size of an RLP list with the given
|
// ListSize returns the encoded size of an RLP list with the given
|
||||||
// content size.
|
// content size.
|
||||||
func ListSize(contentSize uint64) uint64 {
|
func ListSize(contentSize uint64) uint64 {
|
||||||
return uint64(headsize(contentSize)) + contentSize
|
return uint64(headsize(contentSize)) + contentSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntSize returns the encoded size of the integer x. Note: The return type of this
|
||||||
|
// function is 'int' for backwards-compatibility reasons. The result is always positive.
|
||||||
|
func IntSize(x uint64) int {
|
||||||
|
if x < 0x80 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 1 + intsize(x)
|
||||||
|
}
|
||||||
|
|
||||||
// Split returns the content of first RLP value and any
|
// Split returns the content of first RLP value and any
|
||||||
// bytes after the value as subslices of b.
|
// bytes after the value as subslices of b.
|
||||||
func Split(b []byte) (k Kind, content, rest []byte, err error) {
|
func Split(b []byte) (k Kind, content, rest []byte, err error) {
|
||||||
|
|
@ -57,6 +98,32 @@ func SplitString(b []byte) (content, rest []byte, err error) {
|
||||||
return content, rest, nil
|
return content, rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplitUint64 decodes an integer at the beginning of b.
|
||||||
|
// It also returns the remaining data after the integer in 'rest'.
|
||||||
|
func SplitUint64(b []byte) (x uint64, rest []byte, err error) {
|
||||||
|
content, rest, err := SplitString(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, b, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case len(content) == 0:
|
||||||
|
return 0, rest, nil
|
||||||
|
case len(content) == 1:
|
||||||
|
if content[0] == 0 {
|
||||||
|
return 0, b, ErrCanonInt
|
||||||
|
}
|
||||||
|
return uint64(content[0]), rest, nil
|
||||||
|
case len(content) > 8:
|
||||||
|
return 0, b, errUintOverflow
|
||||||
|
default:
|
||||||
|
x, err = readSize(content, byte(len(content)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, b, ErrCanonInt
|
||||||
|
}
|
||||||
|
return x, rest, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SplitList splits b into the content of a list and any remaining
|
// SplitList splits b into the content of a list and any remaining
|
||||||
// bytes after the list.
|
// bytes after the list.
|
||||||
func SplitList(b []byte) (content, rest []byte, err error) {
|
func SplitList(b []byte) (content, rest []byte, err error) {
|
||||||
|
|
|
||||||
160
rlp/raw_test.go
160
rlp/raw_test.go
|
|
@ -18,9 +18,10 @@ package rlp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCountValues(t *testing.T) {
|
func TestCountValues(t *testing.T) {
|
||||||
|
|
@ -53,21 +54,84 @@ func TestCountValues(t *testing.T) {
|
||||||
if count != test.count {
|
if count != test.count {
|
||||||
t.Errorf("test %d: count mismatch, got %d want %d\ninput: %s", i, count, test.count, test.input)
|
t.Errorf("test %d: count mismatch, got %d want %d\ninput: %s", i, count, test.count, test.input)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(err, test.err) {
|
if !errors.Is(err, test.err) {
|
||||||
t.Errorf("test %d: err mismatch, got %q want %q\ninput: %s", i, err, test.err, test.input)
|
t.Errorf("test %d: err mismatch, got %q want %q\ninput: %s", i, err, test.err, test.input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitTypes(t *testing.T) {
|
func TestSplitString(t *testing.T) {
|
||||||
if _, _, err := SplitString(unhex("C100")); err != ErrExpectedString {
|
for i, test := range []string{
|
||||||
t.Errorf("SplitString returned %q, want %q", err, ErrExpectedString)
|
"C0",
|
||||||
|
"C100",
|
||||||
|
"C3010203",
|
||||||
|
"C88363617483646F67",
|
||||||
|
"F8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974",
|
||||||
|
} {
|
||||||
|
if _, _, err := SplitString(unhex(test)); !errors.Is(err, ErrExpectedString) {
|
||||||
|
t.Errorf("test %d: error mismatch: have %q, want %q", i, err, ErrExpectedString)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, _, err := SplitList(unhex("01")); err != ErrExpectedList {
|
}
|
||||||
t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList)
|
|
||||||
|
func TestSplitList(t *testing.T) {
|
||||||
|
for i, test := range []string{
|
||||||
|
"80",
|
||||||
|
"00",
|
||||||
|
"01",
|
||||||
|
"8180",
|
||||||
|
"81FF",
|
||||||
|
"820400",
|
||||||
|
"83636174",
|
||||||
|
"83646F67",
|
||||||
|
"B8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974",
|
||||||
|
} {
|
||||||
|
if _, _, err := SplitList(unhex(test)); !errors.Is(err, ErrExpectedList) {
|
||||||
|
t.Errorf("test %d: error mismatch: have %q, want %q", i, err, ErrExpectedList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, _, err := SplitList(unhex("81FF")); err != ErrExpectedList {
|
}
|
||||||
t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList)
|
|
||||||
|
func TestSplitUint64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
val uint64
|
||||||
|
rest string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{"01", 1, "", nil},
|
||||||
|
{"7FFF", 0x7F, "FF", nil},
|
||||||
|
{"80FF", 0, "FF", nil},
|
||||||
|
{"81FAFF", 0xFA, "FF", nil},
|
||||||
|
{"82FAFAFF", 0xFAFA, "FF", nil},
|
||||||
|
{"83FAFAFAFF", 0xFAFAFA, "FF", nil},
|
||||||
|
{"84FAFAFAFAFF", 0xFAFAFAFA, "FF", nil},
|
||||||
|
{"85FAFAFAFAFAFF", 0xFAFAFAFAFA, "FF", nil},
|
||||||
|
{"86FAFAFAFAFAFAFF", 0xFAFAFAFAFAFA, "FF", nil},
|
||||||
|
{"87FAFAFAFAFAFAFAFF", 0xFAFAFAFAFAFAFA, "FF", nil},
|
||||||
|
{"88FAFAFAFAFAFAFAFAFF", 0xFAFAFAFAFAFAFAFA, "FF", nil},
|
||||||
|
|
||||||
|
// errors
|
||||||
|
{"", 0, "", io.ErrUnexpectedEOF},
|
||||||
|
{"00", 0, "00", ErrCanonInt},
|
||||||
|
{"81", 0, "81", ErrValueTooLarge},
|
||||||
|
{"8100", 0, "8100", ErrCanonSize},
|
||||||
|
{"8200FF", 0, "8200FF", ErrCanonInt},
|
||||||
|
{"8103FF", 0, "8103FF", ErrCanonSize},
|
||||||
|
{"89FAFAFAFAFAFAFAFAFAFF", 0, "89FAFAFAFAFAFAFAFAFAFF", errUintOverflow},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
val, rest, err := SplitUint64(unhex(test.input))
|
||||||
|
if val != test.val {
|
||||||
|
t.Errorf("test %d: val mismatch: got %x, want %x (input %q)", i, val, test.val, test.input)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rest, unhex(test.rest)) {
|
||||||
|
t.Errorf("test %d: rest mismatch: got %x, want %s (input %q)", i, rest, test.rest, test.input)
|
||||||
|
}
|
||||||
|
if err != test.err {
|
||||||
|
t.Errorf("test %d: error mismatch: got %q, want %q", i, err, test.err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +142,9 @@ func TestSplit(t *testing.T) {
|
||||||
val, rest string
|
val, rest string
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
|
{input: "00FFFF", kind: Byte, val: "00", rest: "FFFF"},
|
||||||
{input: "01FFFF", kind: Byte, val: "01", rest: "FFFF"},
|
{input: "01FFFF", kind: Byte, val: "01", rest: "FFFF"},
|
||||||
|
{input: "7FFFFF", kind: Byte, val: "7F", rest: "FFFF"},
|
||||||
{input: "80FFFF", kind: String, val: "", rest: "FFFF"},
|
{input: "80FFFF", kind: String, val: "", rest: "FFFF"},
|
||||||
{input: "C3010203", kind: List, val: "010203"},
|
{input: "C3010203", kind: List, val: "010203"},
|
||||||
|
|
||||||
|
|
@ -194,3 +260,79 @@ func TestReadSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppendUint64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input uint64
|
||||||
|
slice []byte
|
||||||
|
output string
|
||||||
|
}{
|
||||||
|
{0, nil, "80"},
|
||||||
|
{1, nil, "01"},
|
||||||
|
{2, nil, "02"},
|
||||||
|
{127, nil, "7F"},
|
||||||
|
{128, nil, "8180"},
|
||||||
|
{129, nil, "8181"},
|
||||||
|
{0xFFFFFF, nil, "83FFFFFF"},
|
||||||
|
{127, []byte{1, 2, 3}, "0102037F"},
|
||||||
|
{0xFFFFFF, []byte{1, 2, 3}, "01020383FFFFFF"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
x := AppendUint64(test.slice, test.input)
|
||||||
|
if !bytes.Equal(x, unhex(test.output)) {
|
||||||
|
t.Errorf("AppendUint64(%v, %d): got %x, want %s", test.slice, test.input, x, test.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that IntSize returns the appended size.
|
||||||
|
length := len(x) - len(test.slice)
|
||||||
|
if s := IntSize(test.input); s != length {
|
||||||
|
t.Errorf("IntSize(%d): got %d, want %d", test.input, s, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendUint64Random(t *testing.T) {
|
||||||
|
fn := func(i uint64) bool {
|
||||||
|
enc, _ := EncodeToBytes(i)
|
||||||
|
encAppend := AppendUint64(nil, i)
|
||||||
|
return bytes.Equal(enc, encAppend)
|
||||||
|
}
|
||||||
|
config := quick.Config{MaxCountScale: 50}
|
||||||
|
if err := quick.Check(fn, &config); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytesSize(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
v []byte
|
||||||
|
size uint64
|
||||||
|
}{
|
||||||
|
{v: []byte{}, size: 1},
|
||||||
|
{v: []byte{0x1}, size: 1},
|
||||||
|
{v: []byte{0x7E}, size: 1},
|
||||||
|
{v: []byte{0x7F}, size: 1},
|
||||||
|
{v: []byte{0x80}, size: 2},
|
||||||
|
{v: []byte{0xFF}, size: 2},
|
||||||
|
{v: []byte{0xFF, 0xF0}, size: 3},
|
||||||
|
{v: make([]byte, 55), size: 56},
|
||||||
|
{v: make([]byte, 56), size: 58},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
s := BytesSize(test.v)
|
||||||
|
if s != test.size {
|
||||||
|
t.Errorf("BytesSize(%#x) -> %d, want %d", test.v, s, test.size)
|
||||||
|
}
|
||||||
|
s = StringSize(string(test.v))
|
||||||
|
if s != test.size {
|
||||||
|
t.Errorf("StringSize(%#x) -> %d, want %d", test.v, s, test.size)
|
||||||
|
}
|
||||||
|
// Sanity check:
|
||||||
|
enc, _ := EncodeToBytes(test.v)
|
||||||
|
if uint64(len(enc)) != test.size {
|
||||||
|
t.Errorf("len(EncodeToBytes(%#x)) -> %d, test says %d", test.v, len(enc), test.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
800
rlp/rlpgen/gen.go
Normal file
800
rlp/rlpgen/gen.go
Normal file
|
|
@ -0,0 +1,800 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"go/types"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildContext keeps the data needed for make*Op.
|
||||||
|
type buildContext struct {
|
||||||
|
topType *types.Named // the type we're creating methods for
|
||||||
|
|
||||||
|
encoderIface *types.Interface
|
||||||
|
decoderIface *types.Interface
|
||||||
|
rawValueType *types.Named
|
||||||
|
|
||||||
|
typeToStructCache map[types.Type]*rlpstruct.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuildContext(packageRLP *types.Package) *buildContext {
|
||||||
|
enc := packageRLP.Scope().Lookup("Encoder").Type().Underlying()
|
||||||
|
dec := packageRLP.Scope().Lookup("Decoder").Type().Underlying()
|
||||||
|
rawv := packageRLP.Scope().Lookup("RawValue").Type()
|
||||||
|
return &buildContext{
|
||||||
|
typeToStructCache: make(map[types.Type]*rlpstruct.Type),
|
||||||
|
encoderIface: enc.(*types.Interface),
|
||||||
|
decoderIface: dec.(*types.Interface),
|
||||||
|
rawValueType: rawv.(*types.Named),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) isEncoder(typ types.Type) bool {
|
||||||
|
return types.Implements(typ, bctx.encoderIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) isDecoder(typ types.Type) bool {
|
||||||
|
return types.Implements(typ, bctx.decoderIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeToStructType converts typ to rlpstruct.Type.
|
||||||
|
func (bctx *buildContext) typeToStructType(typ types.Type) *rlpstruct.Type {
|
||||||
|
if prev := bctx.typeToStructCache[typ]; prev != nil {
|
||||||
|
return prev // short-circuit for recursive types.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve named types to their underlying type, but keep the name.
|
||||||
|
name := types.TypeString(typ, nil)
|
||||||
|
for {
|
||||||
|
utype := typ.Underlying()
|
||||||
|
if utype == typ {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
typ = utype
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the type and store it in cache.
|
||||||
|
t := &rlpstruct.Type{
|
||||||
|
Name: name,
|
||||||
|
Kind: typeReflectKind(typ),
|
||||||
|
IsEncoder: bctx.isEncoder(typ),
|
||||||
|
IsDecoder: bctx.isDecoder(typ),
|
||||||
|
}
|
||||||
|
bctx.typeToStructCache[typ] = t
|
||||||
|
|
||||||
|
// Assign element type.
|
||||||
|
switch typ.(type) {
|
||||||
|
case *types.Array, *types.Slice, *types.Pointer:
|
||||||
|
etype := typ.(interface{ Elem() types.Type }).Elem()
|
||||||
|
t.Elem = bctx.typeToStructType(etype)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// genContext is passed to the gen* methods of op when generating
|
||||||
|
// the output code. It tracks packages to be imported by the output
|
||||||
|
// file and assigns unique names of temporary variables.
|
||||||
|
type genContext struct {
|
||||||
|
inPackage *types.Package
|
||||||
|
imports map[string]struct{}
|
||||||
|
tempCounter int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGenContext(inPackage *types.Package) *genContext {
|
||||||
|
return &genContext{
|
||||||
|
inPackage: inPackage,
|
||||||
|
imports: make(map[string]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genContext) temp() string {
|
||||||
|
v := fmt.Sprintf("_tmp%d", ctx.tempCounter)
|
||||||
|
ctx.tempCounter++
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genContext) resetTemp() {
|
||||||
|
ctx.tempCounter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genContext) addImport(path string) {
|
||||||
|
if path == ctx.inPackage.Path() {
|
||||||
|
return // avoid importing the package that we're generating in.
|
||||||
|
}
|
||||||
|
// TODO: renaming?
|
||||||
|
ctx.imports[path] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// importsList returns all packages that need to be imported.
|
||||||
|
func (ctx *genContext) importsList() []string {
|
||||||
|
imp := make([]string, 0, len(ctx.imports))
|
||||||
|
for k := range ctx.imports {
|
||||||
|
imp = append(imp, k)
|
||||||
|
}
|
||||||
|
sort.Strings(imp)
|
||||||
|
return imp
|
||||||
|
}
|
||||||
|
|
||||||
|
// qualify is the types.Qualifier used for printing types.
|
||||||
|
func (ctx *genContext) qualify(pkg *types.Package) string {
|
||||||
|
if pkg.Path() == ctx.inPackage.Path() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
ctx.addImport(pkg.Path())
|
||||||
|
// TODO: renaming?
|
||||||
|
return pkg.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
type op interface {
|
||||||
|
// genWrite creates the encoder. The generated code should write v,
|
||||||
|
// which is any Go expression, to the rlp.EncoderBuffer 'w'.
|
||||||
|
genWrite(ctx *genContext, v string) string
|
||||||
|
|
||||||
|
// genDecode creates the decoder. The generated code should read
|
||||||
|
// a value from the rlp.Stream 'dec' and store it to dst.
|
||||||
|
genDecode(ctx *genContext) (string, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// basicOp handles basic types bool, uint*, string.
|
||||||
|
type basicOp struct {
|
||||||
|
typ types.Type
|
||||||
|
writeMethod string // EncoderBuffer writer method name
|
||||||
|
writeArgType types.Type // parameter type of writeMethod
|
||||||
|
decMethod string
|
||||||
|
decResultType types.Type // return type of decMethod
|
||||||
|
decUseBitSize bool // if true, result bit size is appended to decMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*buildContext) makeBasicOp(typ *types.Basic) (op, error) {
|
||||||
|
op := basicOp{typ: typ}
|
||||||
|
kind := typ.Kind()
|
||||||
|
switch {
|
||||||
|
case kind == types.Bool:
|
||||||
|
op.writeMethod = "WriteBool"
|
||||||
|
op.writeArgType = types.Typ[types.Bool]
|
||||||
|
op.decMethod = "Bool"
|
||||||
|
op.decResultType = types.Typ[types.Bool]
|
||||||
|
case kind >= types.Uint8 && kind <= types.Uint64:
|
||||||
|
op.writeMethod = "WriteUint64"
|
||||||
|
op.writeArgType = types.Typ[types.Uint64]
|
||||||
|
op.decMethod = "Uint"
|
||||||
|
op.decResultType = typ
|
||||||
|
op.decUseBitSize = true
|
||||||
|
case kind == types.String:
|
||||||
|
op.writeMethod = "WriteString"
|
||||||
|
op.writeArgType = types.Typ[types.String]
|
||||||
|
op.decMethod = "String"
|
||||||
|
op.decResultType = types.Typ[types.String]
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unhandled basic type: %v", typ)
|
||||||
|
}
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*buildContext) makeByteSliceOp(typ *types.Slice) op {
|
||||||
|
if !isByte(typ.Elem()) {
|
||||||
|
panic("non-byte slice type in makeByteSliceOp")
|
||||||
|
}
|
||||||
|
bslice := types.NewSlice(types.Typ[types.Uint8])
|
||||||
|
return basicOp{
|
||||||
|
typ: typ,
|
||||||
|
writeMethod: "WriteBytes",
|
||||||
|
writeArgType: bslice,
|
||||||
|
decMethod: "Bytes",
|
||||||
|
decResultType: bslice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makeRawValueOp() op {
|
||||||
|
bslice := types.NewSlice(types.Typ[types.Uint8])
|
||||||
|
return basicOp{
|
||||||
|
typ: bctx.rawValueType,
|
||||||
|
writeMethod: "Write",
|
||||||
|
writeArgType: bslice,
|
||||||
|
decMethod: "Raw",
|
||||||
|
decResultType: bslice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op basicOp) writeNeedsConversion() bool {
|
||||||
|
return !types.AssignableTo(op.typ, op.writeArgType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op basicOp) decodeNeedsConversion() bool {
|
||||||
|
return !types.AssignableTo(op.decResultType, op.typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op basicOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
if op.writeNeedsConversion() {
|
||||||
|
v = fmt.Sprintf("%s(%s)", op.writeArgType, v)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("w.%s(%s)\n", op.writeMethod, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op basicOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
var (
|
||||||
|
resultV = ctx.temp()
|
||||||
|
result = resultV
|
||||||
|
method = op.decMethod
|
||||||
|
)
|
||||||
|
if op.decUseBitSize {
|
||||||
|
// Note: For now, this only works for platform-independent integer
|
||||||
|
// sizes. makeBasicOp forbids the platform-dependent types.
|
||||||
|
var sizes types.StdSizes
|
||||||
|
method = fmt.Sprintf("%s%d", op.decMethod, sizes.Sizeof(op.typ)*8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the decoder method.
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "%s, err := dec.%s()\n", resultV, method)
|
||||||
|
fmt.Fprintf(&b, "if err != nil { return err }\n")
|
||||||
|
if op.decodeNeedsConversion() {
|
||||||
|
conv := ctx.temp()
|
||||||
|
fmt.Fprintf(&b, "%s := %s(%s)\n", conv, types.TypeString(op.typ, ctx.qualify), resultV)
|
||||||
|
result = conv
|
||||||
|
}
|
||||||
|
return result, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// byteArrayOp handles [...]byte.
|
||||||
|
type byteArrayOp struct {
|
||||||
|
typ types.Type
|
||||||
|
name types.Type // name != typ for named byte array types (e.g. common.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makeByteArrayOp(name *types.Named, typ *types.Array) byteArrayOp {
|
||||||
|
nt := types.Type(name)
|
||||||
|
if name == nil {
|
||||||
|
nt = typ
|
||||||
|
}
|
||||||
|
return byteArrayOp{typ, nt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op byteArrayOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
return fmt.Sprintf("w.WriteBytes(%s[:])\n", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op byteArrayOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
var resultV = ctx.temp()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "var %s %s\n", resultV, types.TypeString(op.name, ctx.qualify))
|
||||||
|
fmt.Fprintf(&b, "if err := dec.ReadBytes(%s[:]); err != nil { return err }\n", resultV)
|
||||||
|
return resultV, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// bigIntOp handles big.Int.
|
||||||
|
// This exists because big.Int has it's own decoder operation on rlp.Stream,
|
||||||
|
// but the decode method returns *big.Int, so it needs to be dereferenced.
|
||||||
|
type bigIntOp struct {
|
||||||
|
pointer bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op bigIntOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&b, "if %s.Sign() == -1 {\n", v)
|
||||||
|
fmt.Fprintf(&b, " return rlp.ErrNegativeBigInt\n")
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
dst := v
|
||||||
|
if !op.pointer {
|
||||||
|
dst = "&" + v
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&b, "w.WriteBigInt(%s)\n", dst)
|
||||||
|
|
||||||
|
// Wrap with nil check.
|
||||||
|
if op.pointer {
|
||||||
|
code := b.String()
|
||||||
|
b.Reset()
|
||||||
|
fmt.Fprintf(&b, "if %s == nil {\n", v)
|
||||||
|
fmt.Fprintf(&b, " w.Write(rlp.EmptyString)")
|
||||||
|
fmt.Fprintf(&b, "} else {\n")
|
||||||
|
fmt.Fprint(&b, code)
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op bigIntOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
var resultV = ctx.temp()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "%s, err := dec.BigInt()\n", resultV)
|
||||||
|
fmt.Fprintf(&b, "if err != nil { return err }\n")
|
||||||
|
|
||||||
|
result := resultV
|
||||||
|
if !op.pointer {
|
||||||
|
result = "(*" + resultV + ")"
|
||||||
|
}
|
||||||
|
return result, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// uint256Op handles "github.com/holiman/uint256".Int
|
||||||
|
type uint256Op struct {
|
||||||
|
pointer bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op uint256Op) genWrite(ctx *genContext, v string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
dst := v
|
||||||
|
if !op.pointer {
|
||||||
|
dst = "&" + v
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&b, "w.WriteUint256(%s)\n", dst)
|
||||||
|
|
||||||
|
// Wrap with nil check.
|
||||||
|
if op.pointer {
|
||||||
|
code := b.String()
|
||||||
|
b.Reset()
|
||||||
|
fmt.Fprintf(&b, "if %s == nil {\n", v)
|
||||||
|
fmt.Fprintf(&b, " w.Write(rlp.EmptyString)")
|
||||||
|
fmt.Fprintf(&b, "} else {\n")
|
||||||
|
fmt.Fprint(&b, code)
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op uint256Op) genDecode(ctx *genContext) (string, string) {
|
||||||
|
ctx.addImport("github.com/holiman/uint256")
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
resultV := ctx.temp()
|
||||||
|
fmt.Fprintf(&b, "var %s uint256.Int\n", resultV)
|
||||||
|
fmt.Fprintf(&b, "if err := dec.ReadUint256(&%s); err != nil { return err }\n", resultV)
|
||||||
|
|
||||||
|
result := resultV
|
||||||
|
if op.pointer {
|
||||||
|
result = "&" + resultV
|
||||||
|
}
|
||||||
|
return result, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// encoderDecoderOp handles rlp.Encoder and rlp.Decoder.
|
||||||
|
// In order to be used with this, the type must implement both interfaces.
|
||||||
|
// This restriction may be lifted in the future by creating separate ops for
|
||||||
|
// encoding and decoding.
|
||||||
|
type encoderDecoderOp struct {
|
||||||
|
typ types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op encoderDecoderOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
return fmt.Sprintf("if err := %s.EncodeRLP(w); err != nil { return err }\n", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op encoderDecoderOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
// DecodeRLP must have pointer receiver, and this is verified in makeOp.
|
||||||
|
etyp := op.typ.(*types.Pointer).Elem()
|
||||||
|
var resultV = ctx.temp()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "%s := new(%s)\n", resultV, types.TypeString(etyp, ctx.qualify))
|
||||||
|
fmt.Fprintf(&b, "if err := %s.DecodeRLP(dec); err != nil { return err }\n", resultV)
|
||||||
|
return resultV, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptrOp handles pointer types.
|
||||||
|
type ptrOp struct {
|
||||||
|
elemTyp types.Type
|
||||||
|
elem op
|
||||||
|
nilOK bool
|
||||||
|
nilValue rlpstruct.NilKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makePtrOp(elemTyp types.Type, tags rlpstruct.Tags) (op, error) {
|
||||||
|
elemOp, err := bctx.makeOp(nil, elemTyp, rlpstruct.Tags{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
op := ptrOp{elemTyp: elemTyp, elem: elemOp}
|
||||||
|
|
||||||
|
// Determine nil value.
|
||||||
|
if tags.NilOK {
|
||||||
|
op.nilOK = true
|
||||||
|
op.nilValue = tags.NilKind
|
||||||
|
} else {
|
||||||
|
styp := bctx.typeToStructType(elemTyp)
|
||||||
|
op.nilValue = styp.DefaultNilValue()
|
||||||
|
}
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op ptrOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
// Note: in writer functions, accesses to v are read-only, i.e. v is any Go
|
||||||
|
// expression. To make all accesses work through the pointer, we substitute
|
||||||
|
// v with (*v). This is required for most accesses including `v`, `call(v)`,
|
||||||
|
// and `v[index]` on slices.
|
||||||
|
//
|
||||||
|
// For `v.field` and `v[:]` on arrays, the dereference operation is not required.
|
||||||
|
var vv string
|
||||||
|
_, isStruct := op.elem.(structOp)
|
||||||
|
_, isByteArray := op.elem.(byteArrayOp)
|
||||||
|
if isStruct || isByteArray {
|
||||||
|
vv = v
|
||||||
|
} else {
|
||||||
|
vv = fmt.Sprintf("(*%s)", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "if %s == nil {\n", v)
|
||||||
|
fmt.Fprintf(&b, " w.Write([]byte{0x%X})\n", op.nilValue)
|
||||||
|
fmt.Fprintf(&b, "} else {\n")
|
||||||
|
fmt.Fprintf(&b, " %s", op.elem.genWrite(ctx, vv))
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op ptrOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
result, code := op.elem.genDecode(ctx)
|
||||||
|
if !op.nilOK {
|
||||||
|
// If nil pointers are not allowed, we can just decode the element.
|
||||||
|
return "&" + result, code
|
||||||
|
}
|
||||||
|
|
||||||
|
// nil is allowed, so check the kind and size first.
|
||||||
|
// If size is zero and kind matches the nilKind of the type,
|
||||||
|
// the value decodes as a nil pointer.
|
||||||
|
var (
|
||||||
|
resultV = ctx.temp()
|
||||||
|
kindV = ctx.temp()
|
||||||
|
sizeV = ctx.temp()
|
||||||
|
wantKind string
|
||||||
|
)
|
||||||
|
if op.nilValue == rlpstruct.NilKindList {
|
||||||
|
wantKind = "rlp.List"
|
||||||
|
} else {
|
||||||
|
wantKind = "rlp.String"
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "var %s %s\n", resultV, types.TypeString(types.NewPointer(op.elemTyp), ctx.qualify))
|
||||||
|
fmt.Fprintf(&b, "if %s, %s, err := dec.Kind(); err != nil {\n", kindV, sizeV)
|
||||||
|
fmt.Fprintf(&b, " return err\n")
|
||||||
|
fmt.Fprintf(&b, "} else if %s != 0 || %s != %s {\n", sizeV, kindV, wantKind)
|
||||||
|
fmt.Fprint(&b, code)
|
||||||
|
fmt.Fprintf(&b, " %s = &%s\n", resultV, result)
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
return resultV, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// structOp handles struct types.
|
||||||
|
type structOp struct {
|
||||||
|
named *types.Named
|
||||||
|
typ *types.Struct
|
||||||
|
fields []*structField
|
||||||
|
optionalFields []*structField
|
||||||
|
}
|
||||||
|
|
||||||
|
type structField struct {
|
||||||
|
name string
|
||||||
|
typ types.Type
|
||||||
|
elem op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makeStructOp(named *types.Named, typ *types.Struct) (op, error) {
|
||||||
|
// Convert fields to []rlpstruct.Field.
|
||||||
|
var allStructFields []rlpstruct.Field
|
||||||
|
for i := 0; i < typ.NumFields(); i++ {
|
||||||
|
f := typ.Field(i)
|
||||||
|
allStructFields = append(allStructFields, rlpstruct.Field{
|
||||||
|
Name: f.Name(),
|
||||||
|
Exported: f.Exported(),
|
||||||
|
Index: i,
|
||||||
|
Tag: typ.Tag(i),
|
||||||
|
Type: *bctx.typeToStructType(f.Type()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter/validate fields.
|
||||||
|
fields, tags, err := rlpstruct.ProcessFields(allStructFields)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create field ops.
|
||||||
|
var op = structOp{named: named, typ: typ}
|
||||||
|
for i, field := range fields {
|
||||||
|
// Advanced struct tags are not supported yet.
|
||||||
|
tag := tags[i]
|
||||||
|
if err := checkUnsupportedTags(field.Name, tag); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
typ := typ.Field(field.Index).Type()
|
||||||
|
elem, err := bctx.makeOp(nil, typ, tags[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("field %s: %v", field.Name, err)
|
||||||
|
}
|
||||||
|
f := &structField{name: field.Name, typ: typ, elem: elem}
|
||||||
|
if tag.Optional {
|
||||||
|
op.optionalFields = append(op.optionalFields, f)
|
||||||
|
} else {
|
||||||
|
op.fields = append(op.fields, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUnsupportedTags(field string, tag rlpstruct.Tags) error {
|
||||||
|
if tag.Tail {
|
||||||
|
return fmt.Errorf(`field %s has unsupported struct tag "tail"`, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op structOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
var listMarker = ctx.temp()
|
||||||
|
fmt.Fprintf(&b, "%s := w.List()\n", listMarker)
|
||||||
|
for _, field := range op.fields {
|
||||||
|
selector := v + "." + field.name
|
||||||
|
fmt.Fprint(&b, field.elem.genWrite(ctx, selector))
|
||||||
|
}
|
||||||
|
op.writeOptionalFields(&b, ctx, v)
|
||||||
|
fmt.Fprintf(&b, "w.ListEnd(%s)\n", listMarker)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op structOp) writeOptionalFields(b *bytes.Buffer, ctx *genContext, v string) {
|
||||||
|
if len(op.optionalFields) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// First check zero-ness of all optional fields.
|
||||||
|
var zeroV = make([]string, len(op.optionalFields))
|
||||||
|
for i, field := range op.optionalFields {
|
||||||
|
selector := v + "." + field.name
|
||||||
|
zeroV[i] = ctx.temp()
|
||||||
|
fmt.Fprintf(b, "%s := %s\n", zeroV[i], nonZeroCheck(selector, field.typ, ctx.qualify))
|
||||||
|
}
|
||||||
|
// Now write the fields.
|
||||||
|
for i, field := range op.optionalFields {
|
||||||
|
selector := v + "." + field.name
|
||||||
|
cond := ""
|
||||||
|
for j := i; j < len(op.optionalFields); j++ {
|
||||||
|
if j > i {
|
||||||
|
cond += " || "
|
||||||
|
}
|
||||||
|
cond += zeroV[j]
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "if %s {\n", cond)
|
||||||
|
fmt.Fprint(b, field.elem.genWrite(ctx, selector))
|
||||||
|
fmt.Fprintf(b, "}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op structOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
// Get the string representation of the type.
|
||||||
|
// Here, named types are handled separately because the output
|
||||||
|
// would contain a copy of the struct definition otherwise.
|
||||||
|
var typeName string
|
||||||
|
if op.named != nil {
|
||||||
|
typeName = types.TypeString(op.named, ctx.qualify)
|
||||||
|
} else {
|
||||||
|
typeName = types.TypeString(op.typ, ctx.qualify)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create struct object.
|
||||||
|
var resultV = ctx.temp()
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "var %s %s\n", resultV, typeName)
|
||||||
|
|
||||||
|
// Decode fields.
|
||||||
|
fmt.Fprintf(&b, "{\n")
|
||||||
|
fmt.Fprintf(&b, "if _, err := dec.List(); err != nil { return err }\n")
|
||||||
|
for _, field := range op.fields {
|
||||||
|
result, code := field.elem.genDecode(ctx)
|
||||||
|
fmt.Fprintf(&b, "// %s:\n", field.name)
|
||||||
|
fmt.Fprint(&b, code)
|
||||||
|
fmt.Fprintf(&b, "%s.%s = %s\n", resultV, field.name, result)
|
||||||
|
}
|
||||||
|
op.decodeOptionalFields(&b, ctx, resultV)
|
||||||
|
fmt.Fprintf(&b, "if err := dec.ListEnd(); err != nil { return err }\n")
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
return resultV, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op structOp) decodeOptionalFields(b *bytes.Buffer, ctx *genContext, resultV string) {
|
||||||
|
var suffix bytes.Buffer
|
||||||
|
for _, field := range op.optionalFields {
|
||||||
|
result, code := field.elem.genDecode(ctx)
|
||||||
|
fmt.Fprintf(b, "// %s:\n", field.name)
|
||||||
|
fmt.Fprintf(b, "if dec.MoreDataInList() {\n")
|
||||||
|
fmt.Fprint(b, code)
|
||||||
|
fmt.Fprintf(b, "%s.%s = %s\n", resultV, field.name, result)
|
||||||
|
fmt.Fprintf(&suffix, "}\n")
|
||||||
|
}
|
||||||
|
suffix.WriteTo(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceOp handles slice types.
|
||||||
|
type sliceOp struct {
|
||||||
|
typ *types.Slice
|
||||||
|
elemOp op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makeSliceOp(typ *types.Slice) (op, error) {
|
||||||
|
elemOp, err := bctx.makeOp(nil, typ.Elem(), rlpstruct.Tags{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sliceOp{typ: typ, elemOp: elemOp}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op sliceOp) genWrite(ctx *genContext, v string) string {
|
||||||
|
var (
|
||||||
|
listMarker = ctx.temp() // holds return value of w.List()
|
||||||
|
iterElemV = ctx.temp() // iteration variable
|
||||||
|
elemCode = op.elemOp.genWrite(ctx, iterElemV)
|
||||||
|
)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "%s := w.List()\n", listMarker)
|
||||||
|
fmt.Fprintf(&b, "for _, %s := range %s {\n", iterElemV, v)
|
||||||
|
fmt.Fprint(&b, elemCode)
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
fmt.Fprintf(&b, "w.ListEnd(%s)\n", listMarker)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op sliceOp) genDecode(ctx *genContext) (string, string) {
|
||||||
|
var sliceV = ctx.temp() // holds the output slice
|
||||||
|
elemResult, elemCode := op.elemOp.genDecode(ctx)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "var %s %s\n", sliceV, types.TypeString(op.typ, ctx.qualify))
|
||||||
|
fmt.Fprintf(&b, "if _, err := dec.List(); err != nil { return err }\n")
|
||||||
|
fmt.Fprintf(&b, "for dec.MoreDataInList() {\n")
|
||||||
|
fmt.Fprintf(&b, " %s", elemCode)
|
||||||
|
fmt.Fprintf(&b, " %s = append(%s, %s)\n", sliceV, sliceV, elemResult)
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
fmt.Fprintf(&b, "if err := dec.ListEnd(); err != nil { return err }\n")
|
||||||
|
return sliceV, b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstruct.Tags) (op, error) {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types.Named:
|
||||||
|
if isBigInt(typ) {
|
||||||
|
return bigIntOp{}, nil
|
||||||
|
}
|
||||||
|
if isUint256(typ) {
|
||||||
|
return uint256Op{}, nil
|
||||||
|
}
|
||||||
|
if typ == bctx.rawValueType {
|
||||||
|
return bctx.makeRawValueOp(), nil
|
||||||
|
}
|
||||||
|
if bctx.isDecoder(typ) {
|
||||||
|
return nil, fmt.Errorf("type %v implements rlp.Decoder with non-pointer receiver", typ)
|
||||||
|
}
|
||||||
|
// TODO: same check for encoder?
|
||||||
|
return bctx.makeOp(typ, typ.Underlying(), tags)
|
||||||
|
case *types.Pointer:
|
||||||
|
if isBigInt(typ.Elem()) {
|
||||||
|
return bigIntOp{pointer: true}, nil
|
||||||
|
}
|
||||||
|
if isUint256(typ.Elem()) {
|
||||||
|
return uint256Op{pointer: true}, nil
|
||||||
|
}
|
||||||
|
// Encoder/Decoder interfaces.
|
||||||
|
if bctx.isEncoder(typ) {
|
||||||
|
if bctx.isDecoder(typ) {
|
||||||
|
return encoderDecoderOp{typ}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("type %v implements rlp.Encoder but not rlp.Decoder", typ)
|
||||||
|
}
|
||||||
|
if bctx.isDecoder(typ) {
|
||||||
|
return nil, fmt.Errorf("type %v implements rlp.Decoder but not rlp.Encoder", typ)
|
||||||
|
}
|
||||||
|
// Default pointer handling.
|
||||||
|
return bctx.makePtrOp(typ.Elem(), tags)
|
||||||
|
case *types.Basic:
|
||||||
|
return bctx.makeBasicOp(typ)
|
||||||
|
case *types.Struct:
|
||||||
|
return bctx.makeStructOp(name, typ)
|
||||||
|
case *types.Slice:
|
||||||
|
etyp := typ.Elem()
|
||||||
|
if isByte(etyp) && !bctx.isEncoder(etyp) {
|
||||||
|
return bctx.makeByteSliceOp(typ), nil
|
||||||
|
}
|
||||||
|
return bctx.makeSliceOp(typ)
|
||||||
|
case *types.Array:
|
||||||
|
etyp := typ.Elem()
|
||||||
|
if isByte(etyp) && !bctx.isEncoder(etyp) {
|
||||||
|
return bctx.makeByteArrayOp(name, typ), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unhandled array type: %v", typ)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unhandled type: %v", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateDecoder generates the DecodeRLP method on 'typ'.
|
||||||
|
func generateDecoder(ctx *genContext, typ string, op op) []byte {
|
||||||
|
ctx.resetTemp()
|
||||||
|
ctx.addImport(pathOfPackageRLP)
|
||||||
|
|
||||||
|
result, code := op.genDecode(ctx)
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "func (obj *%s) DecodeRLP(dec *rlp.Stream) error {\n", typ)
|
||||||
|
fmt.Fprint(&b, code)
|
||||||
|
fmt.Fprintf(&b, " *obj = %s\n", result)
|
||||||
|
fmt.Fprintf(&b, " return nil\n")
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateEncoder generates the EncodeRLP method on 'typ'.
|
||||||
|
func generateEncoder(ctx *genContext, typ string, op op) []byte {
|
||||||
|
ctx.resetTemp()
|
||||||
|
ctx.addImport("io")
|
||||||
|
ctx.addImport(pathOfPackageRLP)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "func (obj *%s) EncodeRLP(_w io.Writer) error {\n", typ)
|
||||||
|
fmt.Fprintf(&b, " w := rlp.NewEncoderBuffer(_w)\n")
|
||||||
|
fmt.Fprint(&b, op.genWrite(ctx, "obj"))
|
||||||
|
fmt.Fprintf(&b, " return w.Flush()\n")
|
||||||
|
fmt.Fprintf(&b, "}\n")
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bctx *buildContext) generate(typ *types.Named, encoder, decoder bool) ([]byte, error) {
|
||||||
|
bctx.topType = typ
|
||||||
|
|
||||||
|
pkg := typ.Obj().Pkg()
|
||||||
|
op, err := bctx.makeOp(nil, typ, rlpstruct.Tags{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctx = newGenContext(pkg)
|
||||||
|
encSource []byte
|
||||||
|
decSource []byte
|
||||||
|
)
|
||||||
|
if encoder {
|
||||||
|
encSource = generateEncoder(ctx, typ.Obj().Name(), op)
|
||||||
|
}
|
||||||
|
if decoder {
|
||||||
|
decSource = generateDecoder(ctx, typ.Obj().Name(), op)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
fmt.Fprintf(&b, "package %s\n\n", pkg.Name())
|
||||||
|
for _, imp := range ctx.importsList() {
|
||||||
|
fmt.Fprintf(&b, "import %q\n", imp)
|
||||||
|
}
|
||||||
|
if encoder {
|
||||||
|
fmt.Fprintln(&b)
|
||||||
|
b.Write(encSource)
|
||||||
|
}
|
||||||
|
if decoder {
|
||||||
|
fmt.Fprintln(&b)
|
||||||
|
b.Write(decSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
source := b.Bytes()
|
||||||
|
// fmt.Println(string(source))
|
||||||
|
return format.Source(source)
|
||||||
|
}
|
||||||
107
rlp/rlpgen/gen_test.go
Normal file
107
rlp/rlpgen/gen_test.go
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/importer"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package RLP is loaded only once and reused for all tests.
|
||||||
|
var (
|
||||||
|
testFset = token.NewFileSet()
|
||||||
|
testImporter = importer.ForCompiler(testFset, "source", nil).(types.ImporterFrom)
|
||||||
|
testPackageRLP *types.Package
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testPackageRLP, err = testImporter.ImportFrom(pathOfPackageRLP, cwd, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't load package RLP: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256"}
|
||||||
|
|
||||||
|
func TestOutput(t *testing.T) {
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test, func(t *testing.T) {
|
||||||
|
inputFile := filepath.Join("testdata", test+".in.txt")
|
||||||
|
outputFile := filepath.Join("testdata", test+".out.txt")
|
||||||
|
bctx, typ, err := loadTestSource(inputFile, "Test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error loading test source:", err)
|
||||||
|
}
|
||||||
|
output, err := bctx.generate(typ, true, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error in generate:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set this environment variable to regenerate the test outputs.
|
||||||
|
if os.Getenv("WRITE_TEST_FILES") != "" {
|
||||||
|
os.WriteFile(outputFile, output, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if output matches.
|
||||||
|
wantOutput, err := os.ReadFile(outputFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("error loading expected test output:", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(output, wantOutput) {
|
||||||
|
t.Fatalf("output mismatch, want: %v got %v", string(wantOutput), string(output))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadTestSource(file string, typeName string) (*buildContext, *types.Named, error) {
|
||||||
|
// Load the test input.
|
||||||
|
content, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
f, err := parser.ParseFile(testFset, file, content, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
conf := types.Config{Importer: testImporter}
|
||||||
|
pkg, err := conf.Check("test", testFset, []*ast.File{f}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the test struct.
|
||||||
|
bctx := newBuildContext(testPackageRLP)
|
||||||
|
typ, err := lookupStructType(pkg.Scope(), typeName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("can't find type %s: %v", typeName, err)
|
||||||
|
}
|
||||||
|
return bctx, typ, nil
|
||||||
|
}
|
||||||
144
rlp/rlpgen/main.go
Normal file
144
rlp/rlpgen/main.go
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/types"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
|
)
|
||||||
|
|
||||||
|
const pathOfPackageRLP = "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
pkgdir = flag.String("dir", ".", "input package")
|
||||||
|
output = flag.String("out", "-", "output file (default is stdout)")
|
||||||
|
genEncoder = flag.Bool("encoder", true, "generate EncodeRLP?")
|
||||||
|
genDecoder = flag.Bool("decoder", false, "generate DecodeRLP?")
|
||||||
|
typename = flag.String("type", "", "type to generate methods for")
|
||||||
|
)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cfg := Config{
|
||||||
|
Dir: *pkgdir,
|
||||||
|
Type: *typename,
|
||||||
|
GenerateEncoder: *genEncoder,
|
||||||
|
GenerateDecoder: *genDecoder,
|
||||||
|
}
|
||||||
|
code, err := cfg.process()
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
if *output == "-" {
|
||||||
|
os.Stdout.Write(code)
|
||||||
|
} else if err := os.WriteFile(*output, code, 0600); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fatal(args ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Dir string // input package directory
|
||||||
|
Type string
|
||||||
|
|
||||||
|
GenerateEncoder bool
|
||||||
|
GenerateDecoder bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// process generates the Go code.
|
||||||
|
func (cfg *Config) process() (code []byte, err error) {
|
||||||
|
// Load packages.
|
||||||
|
pcfg := &packages.Config{
|
||||||
|
Mode: packages.NeedName | packages.NeedTypes,
|
||||||
|
Dir: cfg.Dir,
|
||||||
|
}
|
||||||
|
ps, err := packages.Load(pcfg, pathOfPackageRLP, ".")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(ps) == 0 {
|
||||||
|
return nil, fmt.Errorf("no Go package found in %s", cfg.Dir)
|
||||||
|
}
|
||||||
|
packages.PrintErrors(ps)
|
||||||
|
|
||||||
|
// Find the packages that were loaded.
|
||||||
|
var (
|
||||||
|
pkg *types.Package
|
||||||
|
packageRLP *types.Package
|
||||||
|
)
|
||||||
|
for _, p := range ps {
|
||||||
|
if len(p.Errors) > 0 {
|
||||||
|
return nil, fmt.Errorf("package %s has errors", p.PkgPath)
|
||||||
|
}
|
||||||
|
if p.PkgPath == pathOfPackageRLP {
|
||||||
|
packageRLP = p.Types
|
||||||
|
} else {
|
||||||
|
pkg = p.Types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bctx := newBuildContext(packageRLP)
|
||||||
|
|
||||||
|
// Find the type and generate.
|
||||||
|
typ, err := lookupStructType(pkg.Scope(), cfg.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't find %s in %s: %v", cfg.Type, pkg, err)
|
||||||
|
}
|
||||||
|
code, err = bctx.generate(typ, cfg.GenerateEncoder, cfg.GenerateDecoder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add build comments.
|
||||||
|
// This is done here to avoid processing these lines with gofmt.
|
||||||
|
var header bytes.Buffer
|
||||||
|
fmt.Fprint(&header, "// Code generated by rlpgen. DO NOT EDIT.\n\n")
|
||||||
|
return append(header.Bytes(), code...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupStructType(scope *types.Scope, name string) (*types.Named, error) {
|
||||||
|
typ, err := lookupType(scope, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, ok := typ.Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("not a struct type")
|
||||||
|
}
|
||||||
|
return typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupType(scope *types.Scope, name string) (*types.Named, error) {
|
||||||
|
obj := scope.Lookup(name)
|
||||||
|
if obj == nil {
|
||||||
|
return nil, errors.New("no such identifier")
|
||||||
|
}
|
||||||
|
typ, ok := obj.(*types.TypeName)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("not a type")
|
||||||
|
}
|
||||||
|
return typ.Type().(*types.Named), nil
|
||||||
|
}
|
||||||
10
rlp/rlpgen/testdata/bigint.in.txt
vendored
Normal file
10
rlp/rlpgen/testdata/bigint.in.txt
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "math/big"
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Int *big.Int
|
||||||
|
IntNoPtr big.Int
|
||||||
|
}
|
||||||
49
rlp/rlpgen/testdata/bigint.out.txt
vendored
Normal file
49
rlp/rlpgen/testdata/bigint.out.txt
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
if obj.Int == nil {
|
||||||
|
w.Write(rlp.EmptyString)
|
||||||
|
} else {
|
||||||
|
if obj.Int.Sign() == -1 {
|
||||||
|
return rlp.ErrNegativeBigInt
|
||||||
|
}
|
||||||
|
w.WriteBigInt(obj.Int)
|
||||||
|
}
|
||||||
|
if obj.IntNoPtr.Sign() == -1 {
|
||||||
|
return rlp.ErrNegativeBigInt
|
||||||
|
}
|
||||||
|
w.WriteBigInt(&obj.IntNoPtr)
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Int:
|
||||||
|
_tmp1, err := dec.BigInt()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Int = _tmp1
|
||||||
|
// IntNoPtr:
|
||||||
|
_tmp2, err := dec.BigInt()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.IntNoPtr = (*_tmp2)
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
30
rlp/rlpgen/testdata/nil.in.txt
vendored
Normal file
30
rlp/rlpgen/testdata/nil.in.txt
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
type Aux struct{
|
||||||
|
A uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct{
|
||||||
|
Uint8 *byte `rlp:"nil"`
|
||||||
|
Uint8List *byte `rlp:"nilList"`
|
||||||
|
|
||||||
|
Uint32 *uint32 `rlp:"nil"`
|
||||||
|
Uint32List *uint32 `rlp:"nilList"`
|
||||||
|
|
||||||
|
Uint64 *uint64 `rlp:"nil"`
|
||||||
|
Uint64List *uint64 `rlp:"nilList"`
|
||||||
|
|
||||||
|
String *string `rlp:"nil"`
|
||||||
|
StringList *string `rlp:"nilList"`
|
||||||
|
|
||||||
|
ByteArray *[3]byte `rlp:"nil"`
|
||||||
|
ByteArrayList *[3]byte `rlp:"nilList"`
|
||||||
|
|
||||||
|
ByteSlice *[]byte `rlp:"nil"`
|
||||||
|
ByteSliceList *[]byte `rlp:"nilList"`
|
||||||
|
|
||||||
|
Struct *Aux `rlp:"nil"`
|
||||||
|
StructString *Aux `rlp:"nilString"`
|
||||||
|
}
|
||||||
289
rlp/rlpgen/testdata/nil.out.txt
vendored
Normal file
289
rlp/rlpgen/testdata/nil.out.txt
vendored
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
if obj.Uint8 == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64(uint64((*obj.Uint8)))
|
||||||
|
}
|
||||||
|
if obj.Uint8List == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64(uint64((*obj.Uint8List)))
|
||||||
|
}
|
||||||
|
if obj.Uint32 == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64(uint64((*obj.Uint32)))
|
||||||
|
}
|
||||||
|
if obj.Uint32List == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64(uint64((*obj.Uint32List)))
|
||||||
|
}
|
||||||
|
if obj.Uint64 == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64((*obj.Uint64))
|
||||||
|
}
|
||||||
|
if obj.Uint64List == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64((*obj.Uint64List))
|
||||||
|
}
|
||||||
|
if obj.String == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteString((*obj.String))
|
||||||
|
}
|
||||||
|
if obj.StringList == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteString((*obj.StringList))
|
||||||
|
}
|
||||||
|
if obj.ByteArray == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes(obj.ByteArray[:])
|
||||||
|
}
|
||||||
|
if obj.ByteArrayList == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes(obj.ByteArrayList[:])
|
||||||
|
}
|
||||||
|
if obj.ByteSlice == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes((*obj.ByteSlice))
|
||||||
|
}
|
||||||
|
if obj.ByteSliceList == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes((*obj.ByteSliceList))
|
||||||
|
}
|
||||||
|
if obj.Struct == nil {
|
||||||
|
w.Write([]byte{0xC0})
|
||||||
|
} else {
|
||||||
|
_tmp1 := w.List()
|
||||||
|
w.WriteUint64(uint64(obj.Struct.A))
|
||||||
|
w.ListEnd(_tmp1)
|
||||||
|
}
|
||||||
|
if obj.StructString == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
_tmp2 := w.List()
|
||||||
|
w.WriteUint64(uint64(obj.StructString.A))
|
||||||
|
w.ListEnd(_tmp2)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Uint8:
|
||||||
|
var _tmp2 *byte
|
||||||
|
if _tmp3, _tmp4, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp4 != 0 || _tmp3 != rlp.String {
|
||||||
|
_tmp1, err := dec.Uint8()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp2 = &_tmp1
|
||||||
|
}
|
||||||
|
_tmp0.Uint8 = _tmp2
|
||||||
|
// Uint8List:
|
||||||
|
var _tmp6 *byte
|
||||||
|
if _tmp7, _tmp8, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp8 != 0 || _tmp7 != rlp.List {
|
||||||
|
_tmp5, err := dec.Uint8()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp6 = &_tmp5
|
||||||
|
}
|
||||||
|
_tmp0.Uint8List = _tmp6
|
||||||
|
// Uint32:
|
||||||
|
var _tmp10 *uint32
|
||||||
|
if _tmp11, _tmp12, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp12 != 0 || _tmp11 != rlp.String {
|
||||||
|
_tmp9, err := dec.Uint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp10 = &_tmp9
|
||||||
|
}
|
||||||
|
_tmp0.Uint32 = _tmp10
|
||||||
|
// Uint32List:
|
||||||
|
var _tmp14 *uint32
|
||||||
|
if _tmp15, _tmp16, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp16 != 0 || _tmp15 != rlp.List {
|
||||||
|
_tmp13, err := dec.Uint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp14 = &_tmp13
|
||||||
|
}
|
||||||
|
_tmp0.Uint32List = _tmp14
|
||||||
|
// Uint64:
|
||||||
|
var _tmp18 *uint64
|
||||||
|
if _tmp19, _tmp20, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp20 != 0 || _tmp19 != rlp.String {
|
||||||
|
_tmp17, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp18 = &_tmp17
|
||||||
|
}
|
||||||
|
_tmp0.Uint64 = _tmp18
|
||||||
|
// Uint64List:
|
||||||
|
var _tmp22 *uint64
|
||||||
|
if _tmp23, _tmp24, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp24 != 0 || _tmp23 != rlp.List {
|
||||||
|
_tmp21, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp22 = &_tmp21
|
||||||
|
}
|
||||||
|
_tmp0.Uint64List = _tmp22
|
||||||
|
// String:
|
||||||
|
var _tmp26 *string
|
||||||
|
if _tmp27, _tmp28, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp28 != 0 || _tmp27 != rlp.String {
|
||||||
|
_tmp25, err := dec.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp26 = &_tmp25
|
||||||
|
}
|
||||||
|
_tmp0.String = _tmp26
|
||||||
|
// StringList:
|
||||||
|
var _tmp30 *string
|
||||||
|
if _tmp31, _tmp32, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp32 != 0 || _tmp31 != rlp.List {
|
||||||
|
_tmp29, err := dec.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp30 = &_tmp29
|
||||||
|
}
|
||||||
|
_tmp0.StringList = _tmp30
|
||||||
|
// ByteArray:
|
||||||
|
var _tmp34 *[3]byte
|
||||||
|
if _tmp35, _tmp36, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp36 != 0 || _tmp35 != rlp.String {
|
||||||
|
var _tmp33 [3]byte
|
||||||
|
if err := dec.ReadBytes(_tmp33[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp34 = &_tmp33
|
||||||
|
}
|
||||||
|
_tmp0.ByteArray = _tmp34
|
||||||
|
// ByteArrayList:
|
||||||
|
var _tmp38 *[3]byte
|
||||||
|
if _tmp39, _tmp40, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp40 != 0 || _tmp39 != rlp.List {
|
||||||
|
var _tmp37 [3]byte
|
||||||
|
if err := dec.ReadBytes(_tmp37[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp38 = &_tmp37
|
||||||
|
}
|
||||||
|
_tmp0.ByteArrayList = _tmp38
|
||||||
|
// ByteSlice:
|
||||||
|
var _tmp42 *[]byte
|
||||||
|
if _tmp43, _tmp44, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp44 != 0 || _tmp43 != rlp.String {
|
||||||
|
_tmp41, err := dec.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp42 = &_tmp41
|
||||||
|
}
|
||||||
|
_tmp0.ByteSlice = _tmp42
|
||||||
|
// ByteSliceList:
|
||||||
|
var _tmp46 *[]byte
|
||||||
|
if _tmp47, _tmp48, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp48 != 0 || _tmp47 != rlp.List {
|
||||||
|
_tmp45, err := dec.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp46 = &_tmp45
|
||||||
|
}
|
||||||
|
_tmp0.ByteSliceList = _tmp46
|
||||||
|
// Struct:
|
||||||
|
var _tmp51 *Aux
|
||||||
|
if _tmp52, _tmp53, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp53 != 0 || _tmp52 != rlp.List {
|
||||||
|
var _tmp49 Aux
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A:
|
||||||
|
_tmp50, err := dec.Uint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp49.A = _tmp50
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp51 = &_tmp49
|
||||||
|
}
|
||||||
|
_tmp0.Struct = _tmp51
|
||||||
|
// StructString:
|
||||||
|
var _tmp56 *Aux
|
||||||
|
if _tmp57, _tmp58, err := dec.Kind(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _tmp58 != 0 || _tmp57 != rlp.String {
|
||||||
|
var _tmp54 Aux
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A:
|
||||||
|
_tmp55, err := dec.Uint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp54.A = _tmp55
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp56 = &_tmp54
|
||||||
|
}
|
||||||
|
_tmp0.StructString = _tmp56
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
rlp/rlpgen/testdata/optional.in.txt
vendored
Normal file
17
rlp/rlpgen/testdata/optional.in.txt
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
type Aux struct {
|
||||||
|
A uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Uint64 uint64 `rlp:"optional"`
|
||||||
|
Pointer *uint64 `rlp:"optional"`
|
||||||
|
String string `rlp:"optional"`
|
||||||
|
Slice []uint64 `rlp:"optional"`
|
||||||
|
Array [3]byte `rlp:"optional"`
|
||||||
|
NamedStruct Aux `rlp:"optional"`
|
||||||
|
AnonStruct struct{ A string } `rlp:"optional"`
|
||||||
|
}
|
||||||
153
rlp/rlpgen/testdata/optional.out.txt
vendored
Normal file
153
rlp/rlpgen/testdata/optional.out.txt
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
_tmp1 := obj.Uint64 != 0
|
||||||
|
_tmp2 := obj.Pointer != nil
|
||||||
|
_tmp3 := obj.String != ""
|
||||||
|
_tmp4 := len(obj.Slice) > 0
|
||||||
|
_tmp5 := obj.Array != ([3]byte{})
|
||||||
|
_tmp6 := obj.NamedStruct != (Aux{})
|
||||||
|
_tmp7 := obj.AnonStruct != (struct{ A string }{})
|
||||||
|
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||||
|
w.WriteUint64(obj.Uint64)
|
||||||
|
}
|
||||||
|
if _tmp2 || _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||||
|
if obj.Pointer == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteUint64((*obj.Pointer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _tmp3 || _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||||
|
w.WriteString(obj.String)
|
||||||
|
}
|
||||||
|
if _tmp4 || _tmp5 || _tmp6 || _tmp7 {
|
||||||
|
_tmp8 := w.List()
|
||||||
|
for _, _tmp9 := range obj.Slice {
|
||||||
|
w.WriteUint64(_tmp9)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp8)
|
||||||
|
}
|
||||||
|
if _tmp5 || _tmp6 || _tmp7 {
|
||||||
|
w.WriteBytes(obj.Array[:])
|
||||||
|
}
|
||||||
|
if _tmp6 || _tmp7 {
|
||||||
|
_tmp10 := w.List()
|
||||||
|
w.WriteUint64(obj.NamedStruct.A)
|
||||||
|
w.ListEnd(_tmp10)
|
||||||
|
}
|
||||||
|
if _tmp7 {
|
||||||
|
_tmp11 := w.List()
|
||||||
|
w.WriteString(obj.AnonStruct.A)
|
||||||
|
w.ListEnd(_tmp11)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Uint64:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
_tmp1, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Uint64 = _tmp1
|
||||||
|
// Pointer:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
_tmp2, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Pointer = &_tmp2
|
||||||
|
// String:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
_tmp3, err := dec.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.String = _tmp3
|
||||||
|
// Slice:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
var _tmp4 []uint64
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
_tmp5, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp4 = append(_tmp4, _tmp5)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Slice = _tmp4
|
||||||
|
// Array:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
var _tmp6 [3]byte
|
||||||
|
if err := dec.ReadBytes(_tmp6[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Array = _tmp6
|
||||||
|
// NamedStruct:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
var _tmp7 Aux
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A:
|
||||||
|
_tmp8, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp7.A = _tmp8
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp0.NamedStruct = _tmp7
|
||||||
|
// AnonStruct:
|
||||||
|
if dec.MoreDataInList() {
|
||||||
|
var _tmp9 struct{ A string }
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A:
|
||||||
|
_tmp10, err := dec.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp9.A = _tmp10
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp0.AnonStruct = _tmp9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
11
rlp/rlpgen/testdata/rawvalue.in.txt
vendored
Normal file
11
rlp/rlpgen/testdata/rawvalue.in.txt
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
RawValue rlp.RawValue
|
||||||
|
PointerToRawValue *rlp.RawValue
|
||||||
|
SliceOfRawValue []rlp.RawValue
|
||||||
|
}
|
||||||
64
rlp/rlpgen/testdata/rawvalue.out.txt
vendored
Normal file
64
rlp/rlpgen/testdata/rawvalue.out.txt
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
w.Write(obj.RawValue)
|
||||||
|
if obj.PointerToRawValue == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.Write((*obj.PointerToRawValue))
|
||||||
|
}
|
||||||
|
_tmp1 := w.List()
|
||||||
|
for _, _tmp2 := range obj.SliceOfRawValue {
|
||||||
|
w.Write(_tmp2)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp1)
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// RawValue:
|
||||||
|
_tmp1, err := dec.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.RawValue = _tmp1
|
||||||
|
// PointerToRawValue:
|
||||||
|
_tmp2, err := dec.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.PointerToRawValue = &_tmp2
|
||||||
|
// SliceOfRawValue:
|
||||||
|
var _tmp3 []rlp.RawValue
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
_tmp4, err := dec.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp3 = append(_tmp3, _tmp4)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.SliceOfRawValue = _tmp3
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
10
rlp/rlpgen/testdata/uint256.in.txt
vendored
Normal file
10
rlp/rlpgen/testdata/uint256.in.txt
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/holiman/uint256"
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Int *uint256.Int
|
||||||
|
IntNoPtr uint256.Int
|
||||||
|
}
|
||||||
44
rlp/rlpgen/testdata/uint256.out.txt
vendored
Normal file
44
rlp/rlpgen/testdata/uint256.out.txt
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "github.com/holiman/uint256"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
if obj.Int == nil {
|
||||||
|
w.Write(rlp.EmptyString)
|
||||||
|
} else {
|
||||||
|
w.WriteUint256(obj.Int)
|
||||||
|
}
|
||||||
|
w.WriteUint256(&obj.IntNoPtr)
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Int:
|
||||||
|
var _tmp1 uint256.Int
|
||||||
|
if err := dec.ReadUint256(&_tmp1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Int = &_tmp1
|
||||||
|
// IntNoPtr:
|
||||||
|
var _tmp2 uint256.Int
|
||||||
|
if err := dec.ReadUint256(&_tmp2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.IntNoPtr = _tmp2
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
10
rlp/rlpgen/testdata/uints.in.txt
vendored
Normal file
10
rlp/rlpgen/testdata/uints.in.txt
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// -*- mode: go -*-
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
type Test struct{
|
||||||
|
A uint8
|
||||||
|
B uint16
|
||||||
|
C uint32
|
||||||
|
D uint64
|
||||||
|
}
|
||||||
53
rlp/rlpgen/testdata/uints.out.txt
vendored
Normal file
53
rlp/rlpgen/testdata/uints.out.txt
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Test) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
w.WriteUint64(uint64(obj.A))
|
||||||
|
w.WriteUint64(uint64(obj.B))
|
||||||
|
w.WriteUint64(uint64(obj.C))
|
||||||
|
w.WriteUint64(obj.D)
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Test) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
var _tmp0 Test
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// A:
|
||||||
|
_tmp1, err := dec.Uint8()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.A = _tmp1
|
||||||
|
// B:
|
||||||
|
_tmp2, err := dec.Uint16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.B = _tmp2
|
||||||
|
// C:
|
||||||
|
_tmp3, err := dec.Uint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.C = _tmp3
|
||||||
|
// D:
|
||||||
|
_tmp4, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.D = _tmp4
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*obj = _tmp0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
124
rlp/rlpgen/types.go
Normal file
124
rlp/rlpgen/types.go
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/types"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// typeReflectKind gives the reflect.Kind that represents typ.
|
||||||
|
func typeReflectKind(typ types.Type) reflect.Kind {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
k := typ.Kind()
|
||||||
|
if k >= types.Bool && k <= types.Complex128 {
|
||||||
|
// value order matches for Bool..Complex128
|
||||||
|
return reflect.Bool + reflect.Kind(k-types.Bool)
|
||||||
|
}
|
||||||
|
if k == types.String {
|
||||||
|
return reflect.String
|
||||||
|
}
|
||||||
|
if k == types.UnsafePointer {
|
||||||
|
return reflect.UnsafePointer
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("unhandled BasicKind %v", k))
|
||||||
|
case *types.Array:
|
||||||
|
return reflect.Array
|
||||||
|
case *types.Chan:
|
||||||
|
return reflect.Chan
|
||||||
|
case *types.Interface:
|
||||||
|
return reflect.Interface
|
||||||
|
case *types.Map:
|
||||||
|
return reflect.Map
|
||||||
|
case *types.Pointer:
|
||||||
|
return reflect.Ptr
|
||||||
|
case *types.Signature:
|
||||||
|
return reflect.Func
|
||||||
|
case *types.Slice:
|
||||||
|
return reflect.Slice
|
||||||
|
case *types.Struct:
|
||||||
|
return reflect.Struct
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unhandled type %T", typ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nonZeroCheck returns the expression that checks whether 'v' is a non-zero value of type 'vtyp'.
|
||||||
|
func nonZeroCheck(v string, vtyp types.Type, qualify types.Qualifier) string {
|
||||||
|
// Resolve type name.
|
||||||
|
typ := resolveUnderlying(vtyp)
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
k := typ.Kind()
|
||||||
|
switch {
|
||||||
|
case k == types.Bool:
|
||||||
|
return v
|
||||||
|
case k >= types.Uint && k <= types.Complex128:
|
||||||
|
return fmt.Sprintf("%s != 0", v)
|
||||||
|
case k == types.String:
|
||||||
|
return fmt.Sprintf(`%s != ""`, v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unhandled BasicKind %v", k))
|
||||||
|
}
|
||||||
|
case *types.Array, *types.Struct:
|
||||||
|
return fmt.Sprintf("%s != (%s{})", v, types.TypeString(vtyp, qualify))
|
||||||
|
case *types.Interface, *types.Pointer, *types.Signature:
|
||||||
|
return fmt.Sprintf("%s != nil", v)
|
||||||
|
case *types.Slice, *types.Map:
|
||||||
|
return fmt.Sprintf("len(%s) > 0", v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unhandled type %T", typ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBigInt checks whether 'typ' is "math/big".Int.
|
||||||
|
func isBigInt(typ types.Type) bool {
|
||||||
|
named, ok := typ.(*types.Named)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
name := named.Obj()
|
||||||
|
return name.Pkg().Path() == "math/big" && name.Name() == "Int"
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUint256 checks whether 'typ' is "github.com/holiman/uint256".Int.
|
||||||
|
func isUint256(typ types.Type) bool {
|
||||||
|
named, ok := typ.(*types.Named)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
name := named.Obj()
|
||||||
|
return name.Pkg().Path() == "github.com/holiman/uint256" && name.Name() == "Int"
|
||||||
|
}
|
||||||
|
|
||||||
|
// isByte checks whether the underlying type of 'typ' is uint8.
|
||||||
|
func isByte(typ types.Type) bool {
|
||||||
|
basic, ok := resolveUnderlying(typ).(*types.Basic)
|
||||||
|
return ok && basic.Kind() == types.Uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveUnderlying(typ types.Type) types.Type {
|
||||||
|
for {
|
||||||
|
t := typ.Underlying()
|
||||||
|
if t == typ {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
typ = t
|
||||||
|
}
|
||||||
|
}
|
||||||
27
rlp/safe.go
Normal file
27
rlp/safe.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2021 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build nacl || js || !cgo
|
||||||
|
// +build nacl js !cgo
|
||||||
|
|
||||||
|
package rlp
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// byteArrayBytes returns a slice of the byte array v.
|
||||||
|
func byteArrayBytes(v reflect.Value, length int) []byte {
|
||||||
|
return v.Slice(0, length).Bytes()
|
||||||
|
}
|
||||||
270
rlp/typecache.go
270
rlp/typecache.go
|
|
@ -18,139 +18,221 @@ package rlp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// typeinfo is an entry in the type cache.
|
||||||
typeCacheMutex sync.RWMutex
|
|
||||||
typeCache = make(map[typekey]*typeinfo)
|
|
||||||
)
|
|
||||||
|
|
||||||
type typeinfo struct {
|
type typeinfo struct {
|
||||||
decoder
|
decoder decoder
|
||||||
writer
|
decoderErr error // error from makeDecoder
|
||||||
}
|
writer writer
|
||||||
|
writerErr error // error from makeWriter
|
||||||
// represents struct tags
|
|
||||||
type tags struct {
|
|
||||||
// rlp:"nil" controls whether empty input results in a nil pointer.
|
|
||||||
nilOK bool
|
|
||||||
// rlp:"tail" controls whether this field swallows additional list
|
|
||||||
// elements. It can only be set for the last field, which must be
|
|
||||||
// of slice type.
|
|
||||||
tail bool
|
|
||||||
// rlp:"-" ignores fields.
|
|
||||||
ignored bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// typekey is the key of a type in typeCache. It includes the struct tags because
|
||||||
|
// they might generate a different decoder.
|
||||||
type typekey struct {
|
type typekey struct {
|
||||||
reflect.Type
|
reflect.Type
|
||||||
// the key must include the struct tags because they
|
rlpstruct.Tags
|
||||||
// might generate a different decoder.
|
|
||||||
tags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type decoder func(*Stream, reflect.Value) error
|
type decoder func(*Stream, reflect.Value) error
|
||||||
|
|
||||||
type writer func(reflect.Value, *encbuf) error
|
type writer func(reflect.Value, *encBuffer) error
|
||||||
|
|
||||||
func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
|
var theTC = newTypeCache()
|
||||||
typeCacheMutex.RLock()
|
|
||||||
info := typeCache[typekey{typ, tags}]
|
type typeCache struct {
|
||||||
typeCacheMutex.RUnlock()
|
cur atomic.Value
|
||||||
if info != nil {
|
|
||||||
return info, nil
|
// This lock synchronizes writers.
|
||||||
}
|
mu sync.Mutex
|
||||||
// not in the cache, need to generate info for this type.
|
next map[typekey]*typeinfo
|
||||||
typeCacheMutex.Lock()
|
|
||||||
defer typeCacheMutex.Unlock()
|
|
||||||
return cachedTypeInfo1(typ, tags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) {
|
func newTypeCache() *typeCache {
|
||||||
|
c := new(typeCache)
|
||||||
|
c.cur.Store(make(map[typekey]*typeinfo))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func cachedDecoder(typ reflect.Type) (decoder, error) {
|
||||||
|
info := theTC.info(typ)
|
||||||
|
return info.decoder, info.decoderErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func cachedWriter(typ reflect.Type) (writer, error) {
|
||||||
|
info := theTC.info(typ)
|
||||||
|
return info.writer, info.writerErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *typeCache) info(typ reflect.Type) *typeinfo {
|
||||||
|
key := typekey{Type: typ}
|
||||||
|
if info := c.cur.Load().(map[typekey]*typeinfo)[key]; info != nil {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in the cache, need to generate info for this type.
|
||||||
|
return c.generate(typ, rlpstruct.Tags{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *typeCache) generate(typ reflect.Type, tags rlpstruct.Tags) *typeinfo {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
cur := c.cur.Load().(map[typekey]*typeinfo)
|
||||||
|
if info := cur[typekey{typ, tags}]; info != nil {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy cur to next.
|
||||||
|
c.next = maps.Clone(cur)
|
||||||
|
|
||||||
|
// Generate.
|
||||||
|
info := c.infoWhileGenerating(typ, tags)
|
||||||
|
|
||||||
|
// next -> cur
|
||||||
|
c.cur.Store(c.next)
|
||||||
|
c.next = nil
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *typeCache) infoWhileGenerating(typ reflect.Type, tags rlpstruct.Tags) *typeinfo {
|
||||||
key := typekey{typ, tags}
|
key := typekey{typ, tags}
|
||||||
info := typeCache[key]
|
if info := c.next[key]; info != nil {
|
||||||
if info != nil {
|
return info
|
||||||
// another goroutine got the write lock first
|
|
||||||
return info, nil
|
|
||||||
}
|
}
|
||||||
// put a dummmy value into the cache before generating.
|
// Put a dummy value into the cache before generating.
|
||||||
// if the generator tries to lookup itself, it will get
|
// If the generator tries to lookup itself, it will get
|
||||||
// the dummy value and won't call itself recursively.
|
// the dummy value and won't call itself recursively.
|
||||||
typeCache[key] = new(typeinfo)
|
info := new(typeinfo)
|
||||||
info, err := genTypeInfo(typ, tags)
|
c.next[key] = info
|
||||||
if err != nil {
|
info.generate(typ, tags)
|
||||||
// remove the dummy value if the generator fails
|
return info
|
||||||
delete(typeCache, key)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
*typeCache[key] = *info
|
|
||||||
return typeCache[key], err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type field struct {
|
type field struct {
|
||||||
index int
|
index int
|
||||||
info *typeinfo
|
info *typeinfo
|
||||||
|
optional bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// structFields resolves the typeinfo of all public fields in a struct type.
|
||||||
func structFields(typ reflect.Type) (fields []field, err error) {
|
func structFields(typ reflect.Type) (fields []field, err error) {
|
||||||
|
// Convert fields to rlpstruct.Field.
|
||||||
|
var allStructFields []rlpstruct.Field
|
||||||
for i := 0; i < typ.NumField(); i++ {
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
if f := typ.Field(i); f.PkgPath == "" { // exported
|
rf := typ.Field(i)
|
||||||
tags, err := parseStructTag(typ, i)
|
allStructFields = append(allStructFields, rlpstruct.Field{
|
||||||
if err != nil {
|
Name: rf.Name,
|
||||||
return nil, err
|
Index: i,
|
||||||
}
|
Exported: rf.PkgPath == "",
|
||||||
if tags.ignored {
|
Tag: string(rf.Tag),
|
||||||
continue
|
Type: *rtypeToStructType(rf.Type, nil),
|
||||||
}
|
})
|
||||||
info, err := cachedTypeInfo1(f.Type, tags)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
// Filter/validate fields.
|
||||||
}
|
structFields, structTags, err := rlpstruct.ProcessFields(allStructFields)
|
||||||
fields = append(fields, field{i, info})
|
if err != nil {
|
||||||
|
if tagErr, ok := err.(rlpstruct.TagError); ok {
|
||||||
|
tagErr.StructType = typ.String()
|
||||||
|
return nil, tagErr
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve typeinfo.
|
||||||
|
for i, sf := range structFields {
|
||||||
|
typ := typ.Field(sf.Index).Type
|
||||||
|
tags := structTags[i]
|
||||||
|
info := theTC.infoWhileGenerating(typ, tags)
|
||||||
|
fields = append(fields, field{sf.Index, info, tags.Optional})
|
||||||
}
|
}
|
||||||
return fields, nil
|
return fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseStructTag(typ reflect.Type, fi int) (tags, error) {
|
// firstOptionalField returns the index of the first field with "optional" tag.
|
||||||
f := typ.Field(fi)
|
func firstOptionalField(fields []field) int {
|
||||||
var ts tags
|
for i, f := range fields {
|
||||||
for _, t := range strings.Split(f.Tag.Get("rlp"), ",") {
|
if f.optional {
|
||||||
switch t = strings.TrimSpace(t); t {
|
return i
|
||||||
case "":
|
|
||||||
case "-":
|
|
||||||
ts.ignored = true
|
|
||||||
case "nil":
|
|
||||||
ts.nilOK = true
|
|
||||||
case "tail":
|
|
||||||
ts.tail = true
|
|
||||||
if fi != typ.NumField()-1 {
|
|
||||||
return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name)
|
|
||||||
}
|
|
||||||
if f.Type.Kind() != reflect.Slice {
|
|
||||||
return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ts, nil
|
return len(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
|
type structFieldError struct {
|
||||||
info = new(typeinfo)
|
typ reflect.Type
|
||||||
if info.decoder, err = makeDecoder(typ, tags); err != nil {
|
field int
|
||||||
return nil, err
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e structFieldError) Error() string {
|
||||||
|
return fmt.Sprintf("%v (struct field %v.%s)", e.err, e.typ, e.typ.Field(e.field).Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *typeinfo) generate(typ reflect.Type, tags rlpstruct.Tags) {
|
||||||
|
i.decoder, i.decoderErr = makeDecoder(typ, tags)
|
||||||
|
i.writer, i.writerErr = makeWriter(typ, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rtypeToStructType converts typ to rlpstruct.Type.
|
||||||
|
func rtypeToStructType(typ reflect.Type, rec map[reflect.Type]*rlpstruct.Type) *rlpstruct.Type {
|
||||||
|
k := typ.Kind()
|
||||||
|
if k == reflect.Invalid {
|
||||||
|
panic("invalid kind")
|
||||||
}
|
}
|
||||||
if info.writer, err = makeWriter(typ, tags); err != nil {
|
|
||||||
return nil, err
|
if prev := rec[typ]; prev != nil {
|
||||||
|
return prev // short-circuit for recursive types
|
||||||
|
}
|
||||||
|
if rec == nil {
|
||||||
|
rec = make(map[reflect.Type]*rlpstruct.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &rlpstruct.Type{
|
||||||
|
Name: typ.String(),
|
||||||
|
Kind: k,
|
||||||
|
IsEncoder: typ.Implements(encoderInterface),
|
||||||
|
IsDecoder: typ.Implements(decoderInterface),
|
||||||
|
}
|
||||||
|
rec[typ] = t
|
||||||
|
if k == reflect.Array || k == reflect.Slice || k == reflect.Ptr {
|
||||||
|
t.Elem = rtypeToStructType(typ.Elem(), rec)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeNilKind gives the RLP value kind for nil pointers to 'typ'.
|
||||||
|
func typeNilKind(typ reflect.Type, tags rlpstruct.Tags) Kind {
|
||||||
|
styp := rtypeToStructType(typ, nil)
|
||||||
|
|
||||||
|
var nk rlpstruct.NilKind
|
||||||
|
if tags.NilOK {
|
||||||
|
nk = tags.NilKind
|
||||||
|
} else {
|
||||||
|
nk = styp.DefaultNilValue()
|
||||||
|
}
|
||||||
|
switch nk {
|
||||||
|
case rlpstruct.NilKindString:
|
||||||
|
return String
|
||||||
|
case rlpstruct.NilKindList:
|
||||||
|
return List
|
||||||
|
default:
|
||||||
|
panic("invalid nil kind value")
|
||||||
}
|
}
|
||||||
return info, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isUint(k reflect.Kind) bool {
|
func isUint(k reflect.Kind) bool {
|
||||||
return k >= reflect.Uint && k <= reflect.Uintptr
|
return k >= reflect.Uint && k <= reflect.Uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isByte(typ reflect.Type) bool {
|
||||||
|
return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface)
|
||||||
|
}
|
||||||
|
|
|
||||||
30
rlp/unsafe.go
Normal file
30
rlp/unsafe.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2021 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build !nacl && !js && cgo
|
||||||
|
// +build !nacl,!js,cgo
|
||||||
|
|
||||||
|
package rlp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// byteArrayBytes returns a slice of the byte array v.
|
||||||
|
func byteArrayBytes(v reflect.Value, length int) []byte {
|
||||||
|
return unsafe.Slice((*byte)(unsafe.Pointer(v.UnsafeAddr())), length)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue