rlp: upgarde package rlp to 2024-05-15

This commit is contained in:
Daniel Liu 2024-05-15 19:28:59 +08:00
parent 9c03d01614
commit f6bddb669f
32 changed files with 4774 additions and 824 deletions

View file

@ -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
}

View file

@ -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))
}

View file

@ -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
View 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)
}

View 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
}

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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
}

View 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
View 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
View 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)
}
}

View file

@ -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) {

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()
}

View file

@ -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
View 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)
}