mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 13:21:37 +00:00
rlp: upgarde package rlp to 2024-05-15
This commit is contained in:
parent
9c03d01614
commit
f6bddb669f
32 changed files with 4774 additions and 824 deletions
695
rlp/decode.go
695
rlp/decode.go
|
|
@ -26,103 +26,80 @@ import (
|
|||
"math/big"
|
||||
"reflect"
|
||||
"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 (
|
||||
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")
|
||||
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
|
||||
// decoding rules or need to decode into private fields.
|
||||
// Decoder is implemented by types that require custom RLP decoding rules or need to decode
|
||||
// into private fields.
|
||||
//
|
||||
// The DecodeRLP method should read one value from the given
|
||||
// Stream. It is not forbidden to read less or more, but it might
|
||||
// be confusing.
|
||||
// The DecodeRLP method should read one value from the given Stream. It is not forbidden to
|
||||
// read less or more, but it might be confusing.
|
||||
type Decoder interface {
|
||||
DecodeRLP(*Stream) error
|
||||
}
|
||||
|
||||
// Decode parses RLP-encoded data from r and stores the result in the
|
||||
// value pointed to by val. Val must be a non-nil pointer. If r does
|
||||
// not implement ByteReader, Decode will do its own buffering.
|
||||
// Decode parses RLP-encoded data from r and stores the result in the value pointed to by
|
||||
// val. Please see package-level documentation for the decoding rules. Val must be a
|
||||
// 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
|
||||
// DecodeRLP.
|
||||
// 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
|
||||
//
|
||||
// To decode into a pointer, Decode will decode into the value pointed
|
||||
// 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)
|
||||
// NewStream(r, limit).Decode(val)
|
||||
func Decode(r io.Reader, val interface{}) error {
|
||||
// TODO: this could use a Stream from a pool.
|
||||
return NewStream(r, 0).Decode(val)
|
||||
stream := streamPool.Get().(*Stream)
|
||||
defer streamPool.Put(stream)
|
||||
|
||||
stream.Reset(r, 0)
|
||||
return stream.Decode(val)
|
||||
}
|
||||
|
||||
// DecodeBytes parses RLP data from b into val.
|
||||
// Please see the documentation of Decode for the decoding rules.
|
||||
// The input must contain exactly one value and no trailing data.
|
||||
// DecodeBytes parses RLP data from b into val. Please see package-level documentation for
|
||||
// the decoding rules. The input must contain exactly one value and no trailing data.
|
||||
func DecodeBytes(b []byte, val interface{}) error {
|
||||
// TODO: this could use a Stream from a pool.
|
||||
r := bytes.NewReader(b)
|
||||
if err := NewStream(r, uint64(len(b))).Decode(val); err != nil {
|
||||
r := (*sliceReader)(&b)
|
||||
|
||||
stream := streamPool.Get().(*Stream)
|
||||
defer streamPool.Put(stream)
|
||||
|
||||
stream.Reset(r, uint64(len(b)))
|
||||
if err := stream.Decode(val); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Len() > 0 {
|
||||
if len(b) > 0 {
|
||||
return ErrMoreThanOneValue
|
||||
}
|
||||
return nil
|
||||
|
|
@ -173,21 +150,26 @@ func addErrorContext(err error, ctx string) error {
|
|||
var (
|
||||
decoderInterface = reflect.TypeOf(new(Decoder)).Elem()
|
||||
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()
|
||||
switch {
|
||||
case typ == rawValueType:
|
||||
return decodeRawValue, nil
|
||||
case typ.Implements(decoderInterface):
|
||||
return decodeDecoder, nil
|
||||
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
|
||||
return decodeDecoderNoPtr, nil
|
||||
case typ.AssignableTo(reflect.PtrTo(bigInt)):
|
||||
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||
return decodeBigInt, nil
|
||||
case typ.AssignableTo(bigInt):
|
||||
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):
|
||||
return decodeUint, nil
|
||||
case kind == reflect.Bool:
|
||||
|
|
@ -198,11 +180,6 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
|
|||
return makeListDecoder(typ, tags)
|
||||
case kind == reflect.Struct:
|
||||
return makeStructDecoder(typ)
|
||||
case kind == reflect.Ptr:
|
||||
if tags.nilOK {
|
||||
return makeOptionalPtrDecoder(typ)
|
||||
}
|
||||
return makePtrDecoder(typ)
|
||||
case kind == reflect.Interface:
|
||||
return decodeInterface, nil
|
||||
default:
|
||||
|
|
@ -252,35 +229,48 @@ func decodeBigIntNoPtr(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)
|
||||
if i == nil {
|
||||
i = new(big.Int)
|
||||
val.Set(reflect.ValueOf(i))
|
||||
}
|
||||
// Reject leading zero bytes
|
||||
if len(b) > 0 && b[0] == 0 {
|
||||
return wrapStreamError(ErrCanonInt, val.Type())
|
||||
|
||||
err := s.decodeBigInt(i)
|
||||
if err != nil {
|
||||
return wrapStreamError(err, val.Type())
|
||||
}
|
||||
i.SetBytes(b)
|
||||
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()
|
||||
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 {
|
||||
return decodeByteArray, nil
|
||||
} else {
|
||||
return decodeByteSlice, nil
|
||||
}
|
||||
return decodeByteSlice, nil
|
||||
}
|
||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
etypeinfo := theTC.infoWhileGenerating(etype, rlpstruct.Tags{})
|
||||
if etypeinfo.decoderErr != nil {
|
||||
return nil, etypeinfo.decoderErr
|
||||
}
|
||||
var dec decoder
|
||||
switch {
|
||||
|
|
@ -288,7 +278,7 @@ func makeListDecoder(typ reflect.Type, tag tags) (decoder, error) {
|
|||
dec = func(s *Stream, val reflect.Value) error {
|
||||
return decodeListArray(s, val, etypeinfo.decoder)
|
||||
}
|
||||
case tag.tail:
|
||||
case tag.Tail:
|
||||
// A slice with "tail" tag can occur as the last field
|
||||
// of a struct and is supposed to swallow all remaining
|
||||
// list elements. The struct decoder already called s.List,
|
||||
|
|
@ -381,25 +371,23 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vlen := val.Len()
|
||||
slice := byteArrayBytes(val, val.Len())
|
||||
switch kind {
|
||||
case Byte:
|
||||
if vlen == 0 {
|
||||
if len(slice) == 0 {
|
||||
return &decodeError{msg: "input string too long", typ: val.Type()}
|
||||
}
|
||||
if vlen > 1 {
|
||||
} else if len(slice) > 1 {
|
||||
return &decodeError{msg: "input string too short", typ: val.Type()}
|
||||
}
|
||||
bv, _ := s.Uint()
|
||||
val.Index(0).SetUint(bv)
|
||||
slice[0] = s.byteval
|
||||
s.kind = -1
|
||||
case String:
|
||||
if uint64(vlen) < size {
|
||||
if uint64(len(slice)) < size {
|
||||
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()}
|
||||
}
|
||||
slice := val.Slice(0, vlen).Interface().([]byte)
|
||||
if err := s.readFull(slice); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -418,13 +406,25 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
|
|||
if err != nil {
|
||||
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) {
|
||||
if _, err := s.List(); err != nil {
|
||||
return wrapStreamError(err, typ)
|
||||
}
|
||||
for _, f := range fields {
|
||||
for i, f := range fields {
|
||||
err := f.info.decoder(s, val.Field(f.index))
|
||||
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}
|
||||
} else if err != nil {
|
||||
return addErrorContext(err, "."+typ.Field(f.index).Name)
|
||||
|
|
@ -435,15 +435,29 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) {
|
|||
return dec, nil
|
||||
}
|
||||
|
||||
// makePtrDecoder creates a decoder that decodes into
|
||||
// the pointer's element type.
|
||||
func makePtrDecoder(typ reflect.Type) (decoder, error) {
|
||||
etype := typ.Elem()
|
||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func zeroFields(structval reflect.Value, fields []field) {
|
||||
for _, f := range fields {
|
||||
fv := structval.Field(f.index)
|
||||
fv.Set(reflect.Zero(fv.Type()))
|
||||
}
|
||||
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
|
||||
if val.IsNil() {
|
||||
newval = reflect.New(etype)
|
||||
|
|
@ -453,30 +467,39 @@ func makePtrDecoder(typ reflect.Type) (decoder, error) {
|
|||
}
|
||||
return err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// makeOptionalPtrDecoder creates a decoder that decodes empty values
|
||||
// as nil. Non-empty values are decoded into a value of the element type,
|
||||
// just like makePtrDecoder does.
|
||||
// makeNilPtrDecoder creates a decoder that decodes empty values as nil. Non-empty
|
||||
// values are decoded into a value of the element type, just like makePtrDecoder does.
|
||||
//
|
||||
// This decoder is used for pointer-typed struct fields with struct tag "nil".
|
||||
func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) {
|
||||
etype := typ.Elem()
|
||||
etypeinfo, err := cachedTypeInfo1(etype, tags{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dec := func(s *Stream, val reflect.Value) (err error) {
|
||||
func makeNilPtrDecoder(etype reflect.Type, etypeinfo *typeinfo, ts rlpstruct.Tags) decoder {
|
||||
typ := reflect.PointerTo(etype)
|
||||
nilPtr := reflect.Zero(typ)
|
||||
|
||||
// Determine the value kind that results in nil pointer.
|
||||
nilKind := typeNilKind(etype, ts)
|
||||
|
||||
return func(s *Stream, val reflect.Value) (err error) {
|
||||
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
|
||||
// position must advance to the next value even though
|
||||
// we don't read anything.
|
||||
s.kind = -1
|
||||
// set the pointer to nil.
|
||||
val.Set(reflect.Zero(typ))
|
||||
return err
|
||||
val.Set(nilPtr)
|
||||
return nil
|
||||
}
|
||||
newval := val
|
||||
if val.IsNil() {
|
||||
|
|
@ -487,7 +510,6 @@ func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) {
|
|||
}
|
||||
return err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
var ifsliceType = reflect.TypeOf([]interface{}{})
|
||||
|
|
@ -516,25 +538,12 @@ func decodeInterface(s *Stream, val reflect.Value) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// This decoder is used for non-pointer values of types
|
||||
// that implement the Decoder interface using a pointer receiver.
|
||||
func decodeDecoderNoPtr(s *Stream, val reflect.Value) error {
|
||||
func decodeDecoder(s *Stream, val reflect.Value) error {
|
||||
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.
|
||||
type Kind int
|
||||
type Kind int8
|
||||
|
||||
const (
|
||||
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
|
||||
// is implemented by e.g. bufio.Reader and bytes.Reader.
|
||||
type ByteReader interface {
|
||||
|
|
@ -600,22 +586,16 @@ type ByteReader interface {
|
|||
type Stream struct {
|
||||
r ByteReader
|
||||
|
||||
// number of bytes remaining to be read from r.
|
||||
remaining uint64
|
||||
limited bool
|
||||
|
||||
// auxiliary buffer for integer decoding
|
||||
uintbuf []byte
|
||||
|
||||
kind Kind // kind of value ahead
|
||||
size uint64 // size of value ahead
|
||||
byteval byte // value of single byte in type tag
|
||||
kinderr error // error from last readKind
|
||||
stack []listpos
|
||||
remaining uint64 // number of bytes remaining to be read from r
|
||||
size uint64 // size of value ahead
|
||||
kinderr error // error from last readKind
|
||||
stack []uint64 // list sizes
|
||||
uintbuf [32]byte // auxiliary buffer for integer decoding
|
||||
kind Kind // kind of value ahead
|
||||
byteval byte // value of single byte in type tag
|
||||
limited bool // true if input limit is in effect
|
||||
}
|
||||
|
||||
type listpos struct{ pos, size uint64 }
|
||||
|
||||
// NewStream creates a new decoding stream reading from r.
|
||||
//
|
||||
// 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.
|
||||
func (s *Stream) Raw() ([]byte, error) {
|
||||
kind, size, err := s.Kind()
|
||||
|
|
@ -685,8 +696,8 @@ func (s *Stream) Raw() ([]byte, error) {
|
|||
s.kind = -1 // rearm Kind
|
||||
return []byte{s.byteval}, nil
|
||||
}
|
||||
// the original header has already been read and is no longer
|
||||
// available. read content and put a new header in front of it.
|
||||
// The original header has already been read and is no longer
|
||||
// available. Read content and put a new header in front of it.
|
||||
start := headsize(size)
|
||||
buf := make([]byte, uint64(start)+size)
|
||||
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
|
||||
// as an unsigned integer. If the input does not contain an RLP string, the
|
||||
// returned error will be ErrExpectedString.
|
||||
//
|
||||
// Deprecated: use s.Uint64 instead.
|
||||
func (s *Stream) Uint() (uint64, error) {
|
||||
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) {
|
||||
kind, size, err := s.Kind()
|
||||
if err != nil {
|
||||
|
|
@ -769,7 +801,14 @@ func (s *Stream) List() (size uint64, err error) {
|
|||
if kind != List {
|
||||
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.size = 0
|
||||
return size, nil
|
||||
|
|
@ -778,22 +817,116 @@ func (s *Stream) List() (size uint64, err error) {
|
|||
// ListEnd returns to the enclosing list.
|
||||
// The input reader must be positioned at the end of a list.
|
||||
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
|
||||
}
|
||||
tos := s.stack[len(s.stack)-1]
|
||||
if tos.pos != tos.size {
|
||||
} else if listLimit > 0 {
|
||||
return errNotAtEOL
|
||||
}
|
||||
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.size = 0
|
||||
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
|
||||
// to by val. Please see the documentation for the Decode function
|
||||
// to learn about the decoding rules.
|
||||
|
|
@ -809,14 +942,14 @@ func (s *Stream) Decode(val interface{}) error {
|
|||
if rval.IsNil() {
|
||||
return errDecodeIntoNil
|
||||
}
|
||||
info, err := cachedTypeInfo(rtyp.Elem(), tags{})
|
||||
decoder, err := cachedDecoder(rtyp.Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = info.decoder(s, rval.Elem())
|
||||
err = decoder(s, rval.Elem())
|
||||
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(), ")"))
|
||||
}
|
||||
return err
|
||||
|
|
@ -839,6 +972,9 @@ func (s *Stream) Reset(r io.Reader, inputLimit uint64) {
|
|||
case *bytes.Reader:
|
||||
s.remaining = uint64(br.Len())
|
||||
s.limited = true
|
||||
case *bytes.Buffer:
|
||||
s.remaining = uint64(br.Len())
|
||||
s.limited = true
|
||||
case *strings.Reader:
|
||||
s.remaining = uint64(br.Len())
|
||||
s.limited = true
|
||||
|
|
@ -857,9 +993,8 @@ func (s *Stream) Reset(r io.Reader, inputLimit uint64) {
|
|||
s.size = 0
|
||||
s.kind = -1
|
||||
s.kinderr = nil
|
||||
if s.uintbuf == nil {
|
||||
s.uintbuf = make([]byte, 8)
|
||||
}
|
||||
s.byteval = 0
|
||||
s.uintbuf = [32]byte{}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// will not advance the input reader and return cached information.
|
||||
func (s *Stream) Kind() (kind Kind, size uint64, err error) {
|
||||
var tos *listpos
|
||||
if len(s.stack) > 0 {
|
||||
tos = &s.stack[len(s.stack)-1]
|
||||
if s.kind >= 0 {
|
||||
return s.kind, s.size, s.kinderr
|
||||
}
|
||||
if s.kind < 0 {
|
||||
s.kinderr = nil
|
||||
// Don't read further if we're at the end of the
|
||||
// innermost list.
|
||||
if tos != nil && tos.pos == tos.size {
|
||||
return 0, 0, EOL
|
||||
}
|
||||
s.kind, s.size, s.kinderr = s.readKind()
|
||||
if s.kinderr == nil {
|
||||
if tos == nil {
|
||||
// At toplevel, check that the value is smaller
|
||||
// than the remaining input length.
|
||||
if s.limited && s.size > s.remaining {
|
||||
s.kinderr = ErrValueTooLarge
|
||||
}
|
||||
} else {
|
||||
// Inside a list, check that the value doesn't overflow the list.
|
||||
if s.size > tos.size-tos.pos {
|
||||
s.kinderr = ErrElemTooLarge
|
||||
}
|
||||
}
|
||||
|
||||
// Check for end of list. This needs to be done here because readKind
|
||||
// checks against the list size, and would return the wrong error.
|
||||
inList, listLimit := s.listLimit()
|
||||
if inList && listLimit == 0 {
|
||||
return 0, 0, EOL
|
||||
}
|
||||
// Read the actual size tag.
|
||||
s.kind, s.size, s.kinderr = s.readKind()
|
||||
if s.kinderr == nil {
|
||||
// Check the data size of the value ahead against input limits. This
|
||||
// is done here because many decoders require allocating an input
|
||||
// buffer matching the value size. Checking it here protects those
|
||||
// decoders from inputs declaring very large value size.
|
||||
if inList && s.size > listLimit {
|
||||
s.kinderr = ErrElemTooLarge
|
||||
} else if s.limited && s.size > s.remaining {
|
||||
s.kinderr = ErrValueTooLarge
|
||||
}
|
||||
}
|
||||
// Note: this might return a sticky error generated
|
||||
// by an earlier call to readKind.
|
||||
return s.kind, s.size, s.kinderr
|
||||
}
|
||||
|
||||
|
|
@ -929,37 +1058,35 @@ func (s *Stream) readKind() (kind Kind, size uint64, err error) {
|
|||
s.byteval = b
|
||||
return Byte, 0, nil
|
||||
case b < 0xB8:
|
||||
// Otherwise, if a string is 0-55 bytes long,
|
||||
// the RLP encoding consists of a single byte with value 0x80 plus the
|
||||
// length of the string followed by the string. The range of the first
|
||||
// byte is thus [0x80, 0xB7].
|
||||
// Otherwise, if a string is 0-55 bytes long, the RLP encoding consists
|
||||
// of a single byte with value 0x80 plus the length of the string
|
||||
// followed by the string. The range of the first byte is thus [0x80, 0xB7].
|
||||
return String, uint64(b - 0x80), nil
|
||||
case b < 0xC0:
|
||||
// If a string is more than 55 bytes long, the
|
||||
// RLP encoding consists of a single byte with value 0xB7 plus the length
|
||||
// of the length of the string in binary form, followed by the length of
|
||||
// the string, followed by the string. For example, a length-1024 string
|
||||
// would be encoded as 0xB90400 followed by the string. The range of
|
||||
// the first byte is thus [0xB8, 0xBF].
|
||||
// If a string is more than 55 bytes long, the RLP encoding consists of a
|
||||
// single byte with value 0xB7 plus the length of the length of the
|
||||
// string in binary form, followed by the length of the string, followed
|
||||
// by the string. For example, a length-1024 string would be encoded as
|
||||
// 0xB90400 followed by the string. The range of the first byte is thus
|
||||
// [0xB8, 0xBF].
|
||||
size, err = s.readUint(b - 0xB7)
|
||||
if err == nil && size < 56 {
|
||||
err = ErrCanonSize
|
||||
}
|
||||
return String, size, err
|
||||
case b < 0xF8:
|
||||
// If the total payload of a list
|
||||
// (i.e. the combined length of all its items) is 0-55 bytes long, the
|
||||
// RLP encoding consists of a single byte with value 0xC0 plus the length
|
||||
// of the list followed by the concatenation of the RLP encodings of the
|
||||
// items. The range of the first byte is thus [0xC0, 0xF7].
|
||||
// If the total payload of a list (i.e. the combined length of all its
|
||||
// items) is 0-55 bytes long, the RLP encoding consists of a single byte
|
||||
// with value 0xC0 plus the length of the list followed by the
|
||||
// concatenation of the RLP encodings of the items. The range of the
|
||||
// first byte is thus [0xC0, 0xF7].
|
||||
return List, uint64(b - 0xC0), nil
|
||||
default:
|
||||
// If the total payload of a list is more than 55 bytes long,
|
||||
// the RLP encoding consists of a single byte with value 0xF7
|
||||
// plus the length of the length of the payload in binary
|
||||
// form, followed by the length of the payload, followed by
|
||||
// the concatenation of the RLP encodings of the items. The
|
||||
// range of the first byte is thus [0xF8, 0xFF].
|
||||
// If the total payload of a list is more than 55 bytes long, the RLP
|
||||
// encoding consists of a single byte with value 0xF7 plus the length of
|
||||
// the length of the payload in binary form, followed by the length of
|
||||
// the payload, followed by the concatenation of the RLP encodings of
|
||||
// the items. The range of the first byte is thus [0xF8, 0xFF].
|
||||
size, err = s.readUint(b - 0xF7)
|
||||
if err == nil && size < 56 {
|
||||
err = ErrCanonSize
|
||||
|
|
@ -977,23 +1104,22 @@ func (s *Stream) readUint(size byte) (uint64, error) {
|
|||
b, err := s.readByte()
|
||||
return uint64(b), err
|
||||
default:
|
||||
buffer := s.uintbuf[:8]
|
||||
clear(buffer)
|
||||
start := int(8 - size)
|
||||
for i := 0; i < start; i++ {
|
||||
s.uintbuf[i] = 0
|
||||
}
|
||||
if err := s.readFull(s.uintbuf[start:]); err != nil {
|
||||
if err := s.readFull(buffer[start:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if s.uintbuf[start] == 0 {
|
||||
// Note: readUint is also used to decode integer
|
||||
// values. The error needs to be adjusted to become
|
||||
// ErrCanonInt in this case.
|
||||
if buffer[start] == 0 {
|
||||
// Note: readUint is also used to decode integer values.
|
||||
// The error needs to be adjusted to become ErrCanonInt in this case.
|
||||
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) {
|
||||
if err := s.willRead(uint64(len(buf))); err != nil {
|
||||
return err
|
||||
|
|
@ -1004,11 +1130,18 @@ func (s *Stream) readFull(buf []byte) (err error) {
|
|||
n += nn
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the underlying stream.
|
||||
func (s *Stream) readByte() (byte, error) {
|
||||
if err := s.willRead(1); err != nil {
|
||||
return 0, err
|
||||
|
|
@ -1020,16 +1153,16 @@ func (s *Stream) readByte() (byte, error) {
|
|||
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 {
|
||||
s.kind = -1 // rearm Kind
|
||||
|
||||
if len(s.stack) > 0 {
|
||||
// check list overflow
|
||||
tos := s.stack[len(s.stack)-1]
|
||||
if n > tos.size-tos.pos {
|
||||
if inList, limit := s.listLimit(); inList {
|
||||
if n > limit {
|
||||
return ErrElemTooLarge
|
||||
}
|
||||
s.stack[len(s.stack)-1].pos += n
|
||||
s.stack[len(s.stack)-1] = limit - n
|
||||
}
|
||||
if s.limited {
|
||||
if n > s.remaining {
|
||||
|
|
@ -1039,3 +1172,31 @@ func (s *Stream) willRead(n uint64) error {
|
|||
}
|
||||
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"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
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) {
|
||||
r := bytes.NewReader(nil)
|
||||
|
||||
|
|
@ -327,6 +371,15 @@ type recstruct struct {
|
|||
Child *recstruct `rlp:"nil"`
|
||||
}
|
||||
|
||||
type bigIntStruct struct {
|
||||
I *big.Int
|
||||
B string
|
||||
}
|
||||
|
||||
type invalidNilTag struct {
|
||||
X []byte `rlp:"nil"`
|
||||
}
|
||||
|
||||
type invalidTail1 struct {
|
||||
A uint `rlp:"tail"`
|
||||
B string
|
||||
|
|
@ -347,19 +400,79 @@ type tailUint struct {
|
|||
Tail []uint `rlp:"tail"`
|
||||
}
|
||||
|
||||
var (
|
||||
veryBigInt = big.NewInt(0).Add(
|
||||
big.NewInt(0).Lsh(big.NewInt(0xFFFFFFFFFFFFFF), 16),
|
||||
big.NewInt(0xFFFF),
|
||||
)
|
||||
)
|
||||
type tailPrivateFields struct {
|
||||
A uint
|
||||
Tail []uint `rlp:"tail"`
|
||||
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
|
||||
B uint `rlp:"-"`
|
||||
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{
|
||||
// booleans
|
||||
{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"},
|
||||
|
||||
// big ints
|
||||
{input: "80", ptr: new(*big.Int), value: big.NewInt(0)},
|
||||
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
|
||||
{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
|
||||
|
||||
// big int errors
|
||||
{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: "8105", ptr: new(big.Int), error: "rlp: non-canonical size information for *big.Int"},
|
||||
{input: "00", ptr: new(*big.Int), error: "rlp: non-canonical integer (leading zero bytes) 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
|
||||
{
|
||||
|
|
@ -446,6 +578,13 @@ var decodeTests = []decodeTest{
|
|||
ptr: new(recstruct),
|
||||
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
|
||||
{
|
||||
|
|
@ -479,20 +618,20 @@ var decodeTests = []decodeTest{
|
|||
error: "rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I",
|
||||
},
|
||||
{
|
||||
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)",
|
||||
input: "C103",
|
||||
ptr: new(intField),
|
||||
error: "rlp: type int is not RLP-serializable (struct field rlp.intField.X)",
|
||||
},
|
||||
{
|
||||
input: "C50102C20102",
|
||||
ptr: new(tailUint),
|
||||
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"
|
||||
{
|
||||
|
|
@ -510,12 +649,192 @@ var decodeTests = []decodeTest{
|
|||
ptr: new(tailRaw),
|
||||
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 "-"
|
||||
{
|
||||
input: "C20102",
|
||||
ptr: new(hasIgnoredField),
|
||||
value: hasIgnoredField{A: 1, C: 2},
|
||||
ptr: new(ignoredField),
|
||||
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
|
||||
|
|
@ -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
|
||||
// implement ReadByte. It is also not recognized by the
|
||||
// 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
|
||||
|
||||
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() {
|
||||
input, _ := hex.DecodeString("C90A1486666F6F626172")
|
||||
|
||||
type example struct {
|
||||
A, B uint
|
||||
private uint // private fields are ignored
|
||||
String string
|
||||
A, B uint
|
||||
String string
|
||||
}
|
||||
|
||||
var s example
|
||||
|
|
@ -708,7 +1116,7 @@ func ExampleDecode() {
|
|||
fmt.Printf("Decoded value: %#v\n", s)
|
||||
}
|
||||
// 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() {
|
||||
|
|
@ -768,7 +1176,7 @@ func ExampleStream() {
|
|||
// [102 111 111 98 97 114] <nil>
|
||||
}
|
||||
|
||||
func BenchmarkDecode(b *testing.B) {
|
||||
func BenchmarkDecodeUints(b *testing.B) {
|
||||
enc := encodeTestSlice(90000)
|
||||
b.SetBytes(int64(len(enc)))
|
||||
b.ReportAllocs()
|
||||
|
|
@ -783,7 +1191,7 @@ func BenchmarkDecode(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeIntSliceReuse(b *testing.B) {
|
||||
func BenchmarkDecodeUintsReused(b *testing.B) {
|
||||
enc := encodeTestSlice(100000)
|
||||
b.SetBytes(int64(len(enc)))
|
||||
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 {
|
||||
s := make([]uint, n)
|
||||
for i := uint(0); i < n; i++ {
|
||||
|
|
@ -811,7 +1278,7 @@ func encodeTestSlice(n uint) []byte {
|
|||
}
|
||||
|
||||
func unhex(str string) []byte {
|
||||
b, err := hex.DecodeString(strings.Replace(str, " ", "", -1))
|
||||
b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
|
||||
if err != nil {
|
||||
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.
|
||||
|
||||
The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily
|
||||
nested arrays of binary data, and RLP is the main encoding method used
|
||||
to serialize objects in Ethereum. The only purpose of RLP is to encode
|
||||
structure; encoding specific atomic data types (eg. strings, ints,
|
||||
floats) is left up to higher-order protocols; in Ethereum integers
|
||||
must be represented in big endian binary form with no leading zeroes
|
||||
(thus making the integer value zero equivalent to the empty byte
|
||||
array).
|
||||
The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily nested arrays of
|
||||
binary data, and RLP is the main encoding method used to serialize objects in Ethereum.
|
||||
The only purpose of RLP is to encode structure; encoding specific atomic data types (eg.
|
||||
strings, ints, floats) is left up to higher-order protocols. In Ethereum integers must be
|
||||
represented in big endian binary form with no leading zeroes (thus making the integer
|
||||
value zero equivalent to the empty string).
|
||||
|
||||
RLP values are distinguished by a type tag. The type tag precedes the
|
||||
value in the input stream and defines the size and kind of the bytes
|
||||
that follow.
|
||||
RLP values are distinguished by a type tag. The type tag precedes the value in the input
|
||||
stream and defines the size and kind of the bytes 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
|
||||
|
|
|
|||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var (
|
||||
// Common encoded values.
|
||||
// These are useful when implementing EncodeRLP.
|
||||
|
||||
// EmptyString is the encoding of an empty string.
|
||||
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
|
||||
// encoding rules or want to encode private fields.
|
||||
type Encoder interface {
|
||||
|
|
@ -49,80 +57,48 @@ type Encoder interface {
|
|||
// perform many small writes in some cases. Consider making w
|
||||
// buffered.
|
||||
//
|
||||
// Encode uses the following type-dependent 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.
|
||||
// Please see package-level documentation of encoding rules.
|
||||
func Encode(w io.Writer, val interface{}) error {
|
||||
if outer, ok := w.(*encbuf); ok {
|
||||
// Encode was called by some type's EncodeRLP.
|
||||
// Avoid copying by writing to the outer encbuf directly.
|
||||
return outer.encode(val)
|
||||
// Optimization: reuse *encBuffer when called by EncodeRLP.
|
||||
if buf := encBufferFromWriter(w); buf != nil {
|
||||
return buf.encode(val)
|
||||
}
|
||||
eb := encbufPool.Get().(*encbuf)
|
||||
defer encbufPool.Put(eb)
|
||||
eb.reset()
|
||||
if err := eb.encode(val); err != nil {
|
||||
|
||||
buf := getEncBuffer()
|
||||
defer encBufferPool.Put(buf)
|
||||
if err := buf.encode(val); err != nil {
|
||||
return err
|
||||
}
|
||||
return eb.toWriter(w)
|
||||
return buf.writeTo(w)
|
||||
}
|
||||
|
||||
// EncodeBytes returns the RLP encoding of val.
|
||||
// Please see the documentation of Encode for the encoding rules.
|
||||
// EncodeToBytes returns the RLP encoding of val.
|
||||
// Please see package-level documentation for the encoding rules.
|
||||
func EncodeToBytes(val interface{}) ([]byte, error) {
|
||||
eb := encbufPool.Get().(*encbuf)
|
||||
defer encbufPool.Put(eb)
|
||||
eb.reset()
|
||||
if err := eb.encode(val); err != nil {
|
||||
buf := getEncBuffer()
|
||||
defer encBufferPool.Put(buf)
|
||||
|
||||
if err := buf.encode(val); err != nil {
|
||||
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
|
||||
// data.
|
||||
//
|
||||
// Please see the documentation of Encode for the encoding rules.
|
||||
func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
|
||||
eb := encbufPool.Get().(*encbuf)
|
||||
eb.reset()
|
||||
if err := eb.encode(val); err != nil {
|
||||
buf := getEncBuffer()
|
||||
if err := buf.encode(val); err != nil {
|
||||
encBufferPool.Put(buf)
|
||||
return 0, nil, err
|
||||
}
|
||||
return eb.size(), &encReader{buf: eb}, nil
|
||||
}
|
||||
|
||||
type encbuf 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 []byte // 9-byte auxiliary buffer for uint encoding
|
||||
// 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.
|
||||
return buf.size(), &encReader{buf: buf}, nil
|
||||
}
|
||||
|
||||
type listhead struct {
|
||||
|
|
@ -151,214 +127,32 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
|
|||
if size < 56 {
|
||||
buf[0] = smalltag + byte(size)
|
||||
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 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)
|
||||
)
|
||||
var encoderInterface = reflect.TypeOf(new(Encoder)).Elem()
|
||||
|
||||
// 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()
|
||||
switch {
|
||||
case typ == rawValueType:
|
||||
return writeRawValue, nil
|
||||
case typ.Implements(encoderInterface):
|
||||
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)):
|
||||
case typ.AssignableTo(reflect.PointerTo(bigInt)):
|
||||
return writeBigIntPtr, nil
|
||||
case typ.AssignableTo(bigInt):
|
||||
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):
|
||||
return writeUint, nil
|
||||
case kind == reflect.Bool:
|
||||
|
|
@ -368,97 +162,116 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) {
|
|||
case kind == reflect.Slice && isByte(typ.Elem()):
|
||||
return writeBytes, nil
|
||||
case kind == reflect.Array && isByte(typ.Elem()):
|
||||
return writeByteArray, nil
|
||||
return makeByteArrayWriter(typ), nil
|
||||
case kind == reflect.Slice || kind == reflect.Array:
|
||||
return makeSliceWriter(typ, ts)
|
||||
case kind == reflect.Struct:
|
||||
return makeStructWriter(typ)
|
||||
case kind == reflect.Ptr:
|
||||
return makePtrWriter(typ)
|
||||
case kind == reflect.Interface:
|
||||
return writeInterface, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func isByte(typ reflect.Type) bool {
|
||||
return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface)
|
||||
}
|
||||
|
||||
func writeRawValue(val reflect.Value, w *encbuf) error {
|
||||
func writeRawValue(val reflect.Value, w *encBuffer) error {
|
||||
w.str = append(w.str, val.Bytes()...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeUint(val reflect.Value, w *encbuf) error {
|
||||
i := 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]...)
|
||||
}
|
||||
func writeUint(val reflect.Value, w *encBuffer) error {
|
||||
w.writeUint64(val.Uint())
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeBool(val reflect.Value, w *encbuf) error {
|
||||
if val.Bool() {
|
||||
w.str = append(w.str, 0x01)
|
||||
} else {
|
||||
w.str = append(w.str, 0x80)
|
||||
}
|
||||
func writeBool(val reflect.Value, w *encBuffer) error {
|
||||
w.writeBool(val.Bool())
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeBigIntPtr(val reflect.Value, w *encbuf) error {
|
||||
func writeBigIntPtr(val reflect.Value, w *encBuffer) error {
|
||||
ptr := val.Interface().(*big.Int)
|
||||
if ptr == nil {
|
||||
w.str = append(w.str, 0x80)
|
||||
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)
|
||||
return writeBigInt(&i, w)
|
||||
if i.Sign() == -1 {
|
||||
return ErrNegativeBigInt
|
||||
}
|
||||
w.writeBigInt(&i)
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeBigInt(i *big.Int, w *encbuf) error {
|
||||
if cmp := i.Cmp(big0); cmp == -1 {
|
||||
return fmt.Errorf("rlp: cannot encode negative *big.Int")
|
||||
} else if cmp == 0 {
|
||||
func writeU256IntPtr(val reflect.Value, w *encBuffer) error {
|
||||
ptr := val.Interface().(*uint256.Int)
|
||||
if ptr == nil {
|
||||
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 {
|
||||
w.encodeString(i.Bytes())
|
||||
w.str = append(w.str, 0x81, b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeBytes(val reflect.Value, w *encbuf) 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 {
|
||||
func writeString(val reflect.Value, w *encBuffer) error {
|
||||
s := val.String()
|
||||
if len(s) == 1 && s[0] <= 0x7f {
|
||||
// fits single byte, no string header
|
||||
|
|
@ -470,27 +283,7 @@ func writeString(val reflect.Value, w *encbuf) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func writeEncoder(val reflect.Value, w *encbuf) 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 {
|
||||
func writeInterface(val reflect.Value, w *encBuffer) error {
|
||||
if val.IsNil() {
|
||||
// Write empty list. This is consistent with the previous RLP
|
||||
// encoder that we had and should therefore avoid any
|
||||
|
|
@ -499,31 +292,51 @@ func writeInterface(val reflect.Value, w *encbuf) error {
|
|||
return nil
|
||||
}
|
||||
eval := val.Elem()
|
||||
ti, err := cachedTypeInfo(eval.Type(), tags{})
|
||||
writer, err := cachedWriter(eval.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ti.writer(eval, w)
|
||||
return writer(eval, w)
|
||||
}
|
||||
|
||||
func makeSliceWriter(typ reflect.Type, ts tags) (writer, error) {
|
||||
etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func makeSliceWriter(typ reflect.Type, ts rlpstruct.Tags) (writer, error) {
|
||||
etypeinfo := theTC.infoWhileGenerating(typ.Elem(), rlpstruct.Tags{})
|
||||
if etypeinfo.writerErr != nil {
|
||||
return nil, etypeinfo.writerErr
|
||||
}
|
||||
writer := func(val reflect.Value, w *encbuf) error {
|
||||
if !ts.tail {
|
||||
defer w.listEnd(w.list())
|
||||
}
|
||||
vlen := val.Len()
|
||||
for i := 0; i < vlen; i++ {
|
||||
if err := etypeinfo.writer(val.Index(i), w); err != nil {
|
||||
return err
|
||||
|
||||
var wfn writer
|
||||
if ts.Tail {
|
||||
// This is for struct tail slices.
|
||||
// w.list is not called for them.
|
||||
wfn = func(val reflect.Value, w *encBuffer) error {
|
||||
vlen := val.Len()
|
||||
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) {
|
||||
|
|
@ -531,56 +344,86 @@ func makeStructWriter(typ reflect.Type) (writer, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer := func(val reflect.Value, w *encbuf) error {
|
||||
lh := w.list()
|
||||
for _, f := range fields {
|
||||
if err := f.info.writer(val.Field(f.index), w); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range fields {
|
||||
if f.info.writerErr != nil {
|
||||
return nil, structFieldError{typ, f.index, f.info.writerErr}
|
||||
}
|
||||
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 writer, nil
|
||||
}
|
||||
|
||||
func makePtrWriter(typ reflect.Type) (writer, error) {
|
||||
etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
func makeEncoderWriter(typ reflect.Type) writer {
|
||||
if typ.Implements(encoderInterface) {
|
||||
return func(val reflect.Value, w *encBuffer) error {
|
||||
return val.Interface().(Encoder).EncodeRLP(w)
|
||||
}
|
||||
}
|
||||
|
||||
writer := func(val reflect.Value, w *encbuf) error {
|
||||
if val.IsNil() {
|
||||
return nilfunc(w)
|
||||
} else {
|
||||
return etypeinfo.writer(val.Elem(), w)
|
||||
w := func(val reflect.Value, w *encBuffer) error {
|
||||
if !val.CanAddr() {
|
||||
// package json simply doesn't call MarshalJSON for this case, 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: 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
|
||||
|
|
|
|||
|
|
@ -22,8 +22,12 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
type testEncoder struct {
|
||||
|
|
@ -32,12 +36,19 @@ type testEncoder struct {
|
|||
|
||||
func (e *testEncoder) EncodeRLP(w io.Writer) error {
|
||||
if e == nil {
|
||||
w.Write([]byte{0, 0, 0, 0})
|
||||
} else if e.err != nil {
|
||||
return e.err
|
||||
} else {
|
||||
w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1})
|
||||
panic("EncodeRLP called on nil value")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +59,13 @@ func (e byteEncoder) EncodeRLP(w io.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type undecodableEncoder func()
|
||||
|
||||
func (f undecodableEncoder) EncodeRLP(w io.Writer) error {
|
||||
w.Write([]byte{0xF5, 0xF5, 0xF5})
|
||||
return nil
|
||||
}
|
||||
|
||||
type encodableReader struct {
|
||||
A, B uint
|
||||
}
|
||||
|
|
@ -102,35 +120,95 @@ var encTests = []encTest{
|
|||
{val: big.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"},
|
||||
{val: big.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"},
|
||||
{
|
||||
val: big.NewInt(0).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")),
|
||||
val: new(big.Int).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")),
|
||||
output: "8F102030405060708090A0B0C0D0E0F2",
|
||||
},
|
||||
{
|
||||
val: big.NewInt(0).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")),
|
||||
val: new(big.Int).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")),
|
||||
output: "9C0100020003000400050006000700080009000A000B000C000D000E01",
|
||||
},
|
||||
{
|
||||
val: big.NewInt(0).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")),
|
||||
val: new(big.Int).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")),
|
||||
output: "A1010000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
val: veryBigInt,
|
||||
output: "89FFFFFFFFFFFFFFFFFF",
|
||||
},
|
||||
{
|
||||
val: veryVeryBigInt,
|
||||
output: "B848FFFFFFFFFFFFFFFFF800000000000000001BFFFFFFFFFFFFFFFFC8000000000000000045FFFFFFFFFFFFFFFFC800000000000000001BFFFFFFFFFFFFFFFFF8000000000000000001",
|
||||
},
|
||||
|
||||
// non-pointer big.Int
|
||||
{val: *big.NewInt(0), output: "80"},
|
||||
{val: *big.NewInt(0xFFFFFF), output: "83FFFFFF"},
|
||||
|
||||
// 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{0}, output: "00"},
|
||||
{val: []byte{0x7E}, output: "7E"},
|
||||
{val: []byte{0x7F}, output: "7F"},
|
||||
{val: []byte{0x80}, output: "8180"},
|
||||
{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"},
|
||||
|
||||
// strings
|
||||
{val: "", output: "80"},
|
||||
{val: "\x7E", output: "7E"},
|
||||
{val: "\x7F", output: "7F"},
|
||||
|
|
@ -203,6 +281,12 @@ var encTests = []encTest{
|
|||
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
|
||||
{val: RawValue(unhex("01")), output: "01"},
|
||||
{val: RawValue(unhex("82FFFF")), output: "82FFFF"},
|
||||
|
|
@ -213,11 +297,34 @@ var encTests = []encTest{
|
|||
{val: simplestruct{A: 3, B: "foo"}, output: "C50383666F6F"},
|
||||
{val: &recstruct{5, nil}, output: "C205C0"},
|
||||
{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")}}, output: "C20102"},
|
||||
{val: &tailRaw{A: 1, Tail: []RawValue{}}, 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
|
||||
{val: (*uint)(nil), output: "80"},
|
||||
|
|
@ -225,26 +332,73 @@ var encTests = []encTest{
|
|||
{val: (*[]byte)(nil), output: "80"},
|
||||
{val: (*[10]byte)(nil), output: "80"},
|
||||
{val: (*big.Int)(nil), output: "80"},
|
||||
{val: (*uint256.Int)(nil), output: "80"},
|
||||
{val: (*[]string)(nil), output: "C0"},
|
||||
{val: (*[10]string)(nil), output: "C0"},
|
||||
{val: (*[]interface{})(nil), output: "C0"},
|
||||
{val: (*[]struct{ uint })(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
|
||||
{val: []io.Reader{reader}, output: "C3C20102"}, // the contained value is a struct
|
||||
|
||||
// Encoder
|
||||
{val: (*testEncoder)(nil), output: "00000000"},
|
||||
{val: (*testEncoder)(nil), output: "C0"},
|
||||
{val: &testEncoder{}, output: "00010001000100010001"},
|
||||
{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.
|
||||
{val: &struct{ TE testEncoder }{testEncoder{}}, output: "CA00010001000100010001"},
|
||||
{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 special case for []byte
|
||||
|
||||
// Verify the error for non-addressable non-pointer Encoder.
|
||||
{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"},
|
||||
}
|
||||
|
||||
|
|
@ -280,6 +434,21 @@ func TestEncodeToBytes(t *testing.T) {
|
|||
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) {
|
||||
runEncTests(t, func(val interface{}) ([]byte, error) {
|
||||
_, r, err := EncodeToReader(val)
|
||||
|
|
@ -338,3 +507,132 @@ func TestEncodeToReaderReturnToPool(t *testing.T) {
|
|||
}
|
||||
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
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rlp
|
||||
package rlp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
||||
type MyCoolType struct {
|
||||
|
|
@ -28,27 +30,19 @@ type MyCoolType struct {
|
|||
|
||||
// EncodeRLP writes x as RLP list [a, b] that omits the Name field.
|
||||
func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) {
|
||||
// Note: the receiver can be a nil pointer. This allows you to
|
||||
// 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
|
||||
return rlp.Encode(w, []uint{x.a, x.b})
|
||||
}
|
||||
|
||||
func ExampleEncoder() {
|
||||
var t *MyCoolType // t is nil pointer to MyCoolType
|
||||
bytes, _ := EncodeToBytes(t)
|
||||
bytes, _ := rlp.EncodeToBytes(t)
|
||||
fmt.Printf("%v → %X\n", t, bytes)
|
||||
|
||||
t = &MyCoolType{Name: "foobar", a: 5, b: 6}
|
||||
bytes, _ = EncodeToBytes(t)
|
||||
bytes, _ = rlp.EncodeToBytes(t)
|
||||
fmt.Printf("%v → %X\n", t, bytes)
|
||||
|
||||
// Output:
|
||||
// <nil> → C28080
|
||||
// <nil> → C0
|
||||
// &{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{})
|
||||
|
||||
// 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
|
||||
// content size.
|
||||
func ListSize(contentSize uint64) uint64 {
|
||||
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
|
||||
// bytes after the value as subslices of b.
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// bytes after the list.
|
||||
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 (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
func TestCountValues(t *testing.T) {
|
||||
|
|
@ -53,21 +54,84 @@ func TestCountValues(t *testing.T) {
|
|||
if count != test.count {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitTypes(t *testing.T) {
|
||||
if _, _, err := SplitString(unhex("C100")); err != ErrExpectedString {
|
||||
t.Errorf("SplitString returned %q, want %q", err, ErrExpectedString)
|
||||
func TestSplitString(t *testing.T) {
|
||||
for i, test := range []string{
|
||||
"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
|
||||
err error
|
||||
}{
|
||||
{input: "00FFFF", kind: Byte, val: "00", 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: "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 (
|
||||
"fmt"
|
||||
"maps"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp/internal/rlpstruct"
|
||||
)
|
||||
|
||||
var (
|
||||
typeCacheMutex sync.RWMutex
|
||||
typeCache = make(map[typekey]*typeinfo)
|
||||
)
|
||||
|
||||
// typeinfo is an entry in the type cache.
|
||||
type typeinfo struct {
|
||||
decoder
|
||||
writer
|
||||
}
|
||||
|
||||
// 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
|
||||
decoder decoder
|
||||
decoderErr error // error from makeDecoder
|
||||
writer writer
|
||||
writerErr error // error from makeWriter
|
||||
}
|
||||
|
||||
// typekey is the key of a type in typeCache. It includes the struct tags because
|
||||
// they might generate a different decoder.
|
||||
type typekey struct {
|
||||
reflect.Type
|
||||
// the key must include the struct tags because they
|
||||
// might generate a different decoder.
|
||||
tags
|
||||
rlpstruct.Tags
|
||||
}
|
||||
|
||||
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) {
|
||||
typeCacheMutex.RLock()
|
||||
info := typeCache[typekey{typ, tags}]
|
||||
typeCacheMutex.RUnlock()
|
||||
if info != nil {
|
||||
return info, nil
|
||||
}
|
||||
// not in the cache, need to generate info for this type.
|
||||
typeCacheMutex.Lock()
|
||||
defer typeCacheMutex.Unlock()
|
||||
return cachedTypeInfo1(typ, tags)
|
||||
var theTC = newTypeCache()
|
||||
|
||||
type typeCache struct {
|
||||
cur atomic.Value
|
||||
|
||||
// This lock synchronizes writers.
|
||||
mu sync.Mutex
|
||||
next map[typekey]*typeinfo
|
||||
}
|
||||
|
||||
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}
|
||||
info := typeCache[key]
|
||||
if info != nil {
|
||||
// another goroutine got the write lock first
|
||||
return info, nil
|
||||
if info := c.next[key]; info != nil {
|
||||
return info
|
||||
}
|
||||
// put a dummmy value into the cache before generating.
|
||||
// if the generator tries to lookup itself, it will get
|
||||
// Put a dummy value into the cache before generating.
|
||||
// If the generator tries to lookup itself, it will get
|
||||
// the dummy value and won't call itself recursively.
|
||||
typeCache[key] = new(typeinfo)
|
||||
info, err := genTypeInfo(typ, tags)
|
||||
if err != nil {
|
||||
// remove the dummy value if the generator fails
|
||||
delete(typeCache, key)
|
||||
return nil, err
|
||||
}
|
||||
*typeCache[key] = *info
|
||||
return typeCache[key], err
|
||||
info := new(typeinfo)
|
||||
c.next[key] = info
|
||||
info.generate(typ, tags)
|
||||
return info
|
||||
}
|
||||
|
||||
type field struct {
|
||||
index int
|
||||
info *typeinfo
|
||||
index int
|
||||
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) {
|
||||
// Convert fields to rlpstruct.Field.
|
||||
var allStructFields []rlpstruct.Field
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if f := typ.Field(i); f.PkgPath == "" { // exported
|
||||
tags, err := parseStructTag(typ, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tags.ignored {
|
||||
continue
|
||||
}
|
||||
info, err := cachedTypeInfo1(f.Type, tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, field{i, info})
|
||||
rf := typ.Field(i)
|
||||
allStructFields = append(allStructFields, rlpstruct.Field{
|
||||
Name: rf.Name,
|
||||
Index: i,
|
||||
Exported: rf.PkgPath == "",
|
||||
Tag: string(rf.Tag),
|
||||
Type: *rtypeToStructType(rf.Type, nil),
|
||||
})
|
||||
}
|
||||
|
||||
// Filter/validate fields.
|
||||
structFields, structTags, err := rlpstruct.ProcessFields(allStructFields)
|
||||
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
|
||||
}
|
||||
|
||||
func parseStructTag(typ reflect.Type, fi int) (tags, error) {
|
||||
f := typ.Field(fi)
|
||||
var ts tags
|
||||
for _, t := range strings.Split(f.Tag.Get("rlp"), ",") {
|
||||
switch t = strings.TrimSpace(t); t {
|
||||
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)
|
||||
// firstOptionalField returns the index of the first field with "optional" tag.
|
||||
func firstOptionalField(fields []field) int {
|
||||
for i, f := range fields {
|
||||
if f.optional {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return ts, nil
|
||||
return len(fields)
|
||||
}
|
||||
|
||||
func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
|
||||
info = new(typeinfo)
|
||||
if info.decoder, err = makeDecoder(typ, tags); err != nil {
|
||||
return nil, err
|
||||
type structFieldError struct {
|
||||
typ reflect.Type
|
||||
field int
|
||||
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 {
|
||||
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