forked from mirror/go-ethereum
Compare commits
11 Commits
release/1.
...
master
Author | SHA1 | Date |
---|---|---|
|
687e4dc855 | 3 years ago |
|
0cb4d65f8d | 3 years ago |
|
862f8e98bc | 3 years ago |
|
d6f49bf764 | 3 years ago |
|
06aaeed1a6 | 3 years ago |
|
9b93564e21 | 3 years ago |
|
4335bbbf0a | 3 years ago |
|
fc8ad1b70d | 3 years ago |
|
4d086430bd | 3 years ago |
|
2056e596f2 | 3 years ago |
|
55430b6ea2 | 3 years ago |
@ -0,0 +1,27 @@ |
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types |
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp" |
||||
import "io" |
||||
|
||||
func (obj *StateAccount) EncodeRLP(_w io.Writer) error { |
||||
w := rlp.NewEncoderBuffer(_w) |
||||
_tmp0 := w.List() |
||||
w.WriteUint64(obj.Nonce) |
||||
if obj.Balance == nil { |
||||
w.Write(rlp.EmptyString) |
||||
} else { |
||||
if obj.Balance.Sign() == -1 { |
||||
return rlp.ErrNegativeBigInt |
||||
} |
||||
w.WriteBigInt(obj.Balance) |
||||
} |
||||
w.WriteBytes(obj.Root[:]) |
||||
w.WriteBytes(obj.CodeHash) |
||||
w.ListEnd(_tmp0) |
||||
return w.Flush() |
||||
} |
@ -0,0 +1,56 @@ |
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types |
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp" |
||||
import "io" |
||||
|
||||
func (obj *Header) EncodeRLP(_w io.Writer) error { |
||||
w := rlp.NewEncoderBuffer(_w) |
||||
_tmp0 := w.List() |
||||
w.WriteBytes(obj.ParentHash[:]) |
||||
w.WriteBytes(obj.UncleHash[:]) |
||||
w.WriteBytes(obj.Coinbase[:]) |
||||
w.WriteBytes(obj.Root[:]) |
||||
w.WriteBytes(obj.TxHash[:]) |
||||
w.WriteBytes(obj.ReceiptHash[:]) |
||||
w.WriteBytes(obj.Bloom[:]) |
||||
if obj.Difficulty == nil { |
||||
w.Write(rlp.EmptyString) |
||||
} else { |
||||
if obj.Difficulty.Sign() == -1 { |
||||
return rlp.ErrNegativeBigInt |
||||
} |
||||
w.WriteBigInt(obj.Difficulty) |
||||
} |
||||
if obj.Number == nil { |
||||
w.Write(rlp.EmptyString) |
||||
} else { |
||||
if obj.Number.Sign() == -1 { |
||||
return rlp.ErrNegativeBigInt |
||||
} |
||||
w.WriteBigInt(obj.Number) |
||||
} |
||||
w.WriteUint64(obj.GasLimit) |
||||
w.WriteUint64(obj.GasUsed) |
||||
w.WriteUint64(obj.Time) |
||||
w.WriteBytes(obj.Extra) |
||||
w.WriteBytes(obj.MixDigest[:]) |
||||
w.WriteBytes(obj.Nonce[:]) |
||||
_tmp1 := obj.BaseFee != nil |
||||
if _tmp1 { |
||||
if obj.BaseFee == nil { |
||||
w.Write(rlp.EmptyString) |
||||
} else { |
||||
if obj.BaseFee.Sign() == -1 { |
||||
return rlp.ErrNegativeBigInt |
||||
} |
||||
w.WriteBigInt(obj.BaseFee) |
||||
} |
||||
} |
||||
w.ListEnd(_tmp0) |
||||
return w.Flush() |
||||
} |
@ -0,0 +1,23 @@ |
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types |
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp" |
||||
import "io" |
||||
|
||||
func (obj *rlpLog) EncodeRLP(_w io.Writer) error { |
||||
w := rlp.NewEncoderBuffer(_w) |
||||
_tmp0 := w.List() |
||||
w.WriteBytes(obj.Address[:]) |
||||
_tmp1 := w.List() |
||||
for _, _tmp2 := range obj.Topics { |
||||
w.WriteBytes(_tmp2[:]) |
||||
} |
||||
w.ListEnd(_tmp1) |
||||
w.WriteBytes(obj.Data) |
||||
w.ListEnd(_tmp0) |
||||
return w.Flush() |
||||
} |
@ -0,0 +1,361 @@ |
||||
package rlp |
||||
|
||||
import ( |
||||
"io" |
||||
"math/big" |
||||
"reflect" |
||||
"sync" |
||||
) |
||||
|
||||
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 |
||||
} |
||||
|
||||
// toBytes creates the encoder output.
|
||||
func (w *encBuffer) 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 |
||||
} |
||||
|
||||
// toWriter writes the encoder output to w.
|
||||
func (buf *encBuffer) toWriter(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 (w *encBuffer) writeBigInt(i *big.Int) { |
||||
bitlen := i.BitLen() |
||||
if bitlen <= 64 { |
||||
w.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 |
||||
w.encodeStringHeader(length) |
||||
w.str = append(w.str, make([]byte, length)...) |
||||
index := length |
||||
buf := w.str[len(w.str)-length:] |
||||
for _, d := range i.Bits() { |
||||
for j := 0; j < wordBytes && index > 0; j++ { |
||||
index-- |
||||
buf[index] = byte(d) |
||||
d >>= 8 |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
} |
||||
|
||||
// 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, ok := dst.(*encBuffer); ok { |
||||
*w = EncoderBuffer{outer, nil, false} |
||||
return |
||||
} |
||||
if outer, ok := dst.(EncoderBuffer); ok { |
||||
*w = EncoderBuffer{outer.buf, 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.toWriter(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.toBytes() |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// WriteBytes encodes b as an RLP string.
|
||||
func (w EncoderBuffer) WriteBytes(b []byte) { |
||||
w.buf.writeBytes(b) |
||||
} |
||||
|
||||
// WriteBytes 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) |
||||
} |
@ -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/ethereum/go-ethereum/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
|
||||
} |
@ -0,0 +1,213 @@ |
||||
// 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/>.
|
||||
|
||||
// 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) |
||||
} |
@ -0,0 +1,735 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"go/format" |
||||
"go/types" |
||||
"sort" |
||||
|
||||
"github.com/ethereum/go-ethereum/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 // calle write the value
|
||||
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() |
||||
} |
||||
|
||||
// bigIntNoPtrOp handles non-pointer 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() |
||||
} |
||||
|
||||
// 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 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 |
||||
} |
||||
// 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) |
||||
} |
@ -0,0 +1,92 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"go/ast" |
||||
"go/importer" |
||||
"go/parser" |
||||
"go/token" |
||||
"go/types" |
||||
"io/ioutil" |
||||
"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"} |
||||
|
||||
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") != "" { |
||||
ioutil.WriteFile(outputFile, output, 0644) |
||||
} |
||||
|
||||
// Check if output matches.
|
||||
wantOutput, err := ioutil.ReadFile(outputFile) |
||||
if err != nil { |
||||
t.Fatal("error loading expected test output:", err) |
||||
} |
||||
if !bytes.Equal(output, wantOutput) { |
||||
t.Fatal("output mismatch:\n", string(output)) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func loadTestSource(file string, typeName string) (*buildContext, *types.Named, error) { |
||||
// Load the test input.
|
||||
content, err := ioutil.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 |
||||
} |
@ -0,0 +1,148 @@ |
||||
// 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/>.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"flag" |
||||
"fmt" |
||||
"go/types" |
||||
"io/ioutil" |
||||
"os" |
||||
|
||||
"golang.org/x/tools/go/packages" |
||||
) |
||||
|
||||
const pathOfPackageRLP = "github.com/ethereum/go-ethereum/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 := ioutil.WriteFile(*output, code, 0644); 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 | packages.NeedImports | packages.NeedDeps, |
||||
Dir: cfg.Dir, |
||||
BuildFlags: []string{"-tags", "norlpgen"}, |
||||
} |
||||
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", typ, 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") |
||||
fmt.Fprint(&header, "//go:build !norlpgen\n") |
||||
fmt.Fprint(&header, "// +build !norlpgen\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 |
||||
} |
@ -0,0 +1,10 @@ |
||||
// -*- mode: go -*- |
||||
|
||||
package test |
||||
|
||||
import "math/big" |
||||
|
||||
type Test struct { |
||||
Int *big.Int |
||||
IntNoPtr big.Int |
||||
} |
@ -0,0 +1,49 @@ |
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/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 |
||||
} |
@ -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"` |
||||
} |
@ -0,0 +1,289 @@ |
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/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 |
||||
} |
@ -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"` |
||||
} |
@ -0,0 +1,153 @@ |
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/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 |
||||
} |
@ -0,0 +1,11 @@ |
||||
// -*- mode: go -*- |
||||
|
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp" |
||||
|
||||
type Test struct { |
||||
RawValue rlp.RawValue |
||||
PointerToRawValue *rlp.RawValue |
||||
SliceOfRawValue []rlp.RawValue |
||||
} |
@ -0,0 +1,64 @@ |
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/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 |
||||
} |
@ -0,0 +1,10 @@ |
||||
// -*- mode: go -*- |
||||
|
||||
package test |
||||
|
||||
type Test struct{ |
||||
A uint8 |
||||
B uint16 |
||||
C uint32 |
||||
D uint64 |
||||
} |
@ -0,0 +1,53 @@ |
||||
package test |
||||
|
||||
import "github.com/ethereum/go-ethereum/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 |
||||
} |
@ -0,0 +1,98 @@ |
||||
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" |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
} |
Loading…
Reference in new issue