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