Official Go implementation of the Ethereum protocol
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-ethereum/ethutil/rlp.go

419 lines
8.3 KiB

11 years ago
package ethutil
import (
"bytes"
_ "encoding/binary"
"fmt"
_ "log"
_ "math"
"math/big"
"reflect"
)
///////////////////////////////////////
type EthEncoder interface {
EncodeData(rlpData interface{}) []byte
}
type EthDecoder interface {
Get(idx int) *RlpValue
}
//////////////////////////////////////
type RlpEncoder struct {
rlpData []byte
}
func NewRlpEncoder() *RlpEncoder {
encoder := &RlpEncoder{}
return encoder
}
func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
return Encode(rlpData)
}
// Data rlpValueutes are returned by the rlp decoder. The data rlpValueutes represents
// one item within the rlp data structure. It's responsible for all the casting
// It always returns something rlpValueid
type RlpValue struct {
Value interface{}
kind reflect.Value
}
func (rlpValue *RlpValue) String() string {
return fmt.Sprintf("%q", rlpValue.Value)
}
func Conv(rlpValue interface{}) *RlpValue {
return &RlpValue{Value: rlpValue, kind: reflect.ValueOf(rlpValue)}
}
func NewRlpValue(rlpValue interface{}) *RlpValue {
return &RlpValue{Value: rlpValue}
}
func (rlpValue *RlpValue) Type() reflect.Kind {
return reflect.TypeOf(rlpValue.Value).Kind()
}
func (rlpValue *RlpValue) IsNil() bool {
return rlpValue.Value == nil
}
func (rlpValue *RlpValue) Length() int {
//return rlpValue.kind.Len()
if data, ok := rlpValue.Value.([]interface{}); ok {
return len(data)
}
return 0
}
func (rlpValue *RlpValue) AsRaw() interface{} {
return rlpValue.Value
}
func (rlpValue *RlpValue) AsUint() uint64 {
if Value, ok := rlpValue.Value.(uint8); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint16); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint32); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint64); ok {
return Value
}
return 0
}
func (rlpValue *RlpValue) AsByte() byte {
if Value, ok := rlpValue.Value.(byte); ok {
return Value
}
return 0x0
}
func (rlpValue *RlpValue) AsBigInt() *big.Int {
if a, ok := rlpValue.Value.([]byte); ok {
b := new(big.Int)
b.SetBytes(a)
return b
}
return big.NewInt(0)
}
func (rlpValue *RlpValue) AsString() string {
if a, ok := rlpValue.Value.([]byte); ok {
return string(a)
} else if a, ok := rlpValue.Value.(string); ok {
return a
} else {
//panic(fmt.Sprintf("not string %T: %v", rlpValue.Value, rlpValue.Value))
}
return ""
}
func (rlpValue *RlpValue) AsBytes() []byte {
if a, ok := rlpValue.Value.([]byte); ok {
return a
}
return make([]byte, 0)
}
func (rlpValue *RlpValue) AsSlice() []interface{} {
if d, ok := rlpValue.Value.([]interface{}); ok {
return d
}
return []interface{}{}
}
func (rlpValue *RlpValue) AsSliceFrom(from int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[from:])
}
func (rlpValue *RlpValue) AsSliceTo(to int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[:to])
}
func (rlpValue *RlpValue) AsSliceFromTo(from, to int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[from:to])
}
// Threat the rlpValueute as a slice
func (rlpValue *RlpValue) Get(idx int) *RlpValue {
if d, ok := rlpValue.Value.([]interface{}); ok {
// Guard for oob
if len(d) <= idx {
return NewRlpValue(nil)
}
if idx < 0 {
panic("negative idx for Rlp Get")
}
return NewRlpValue(d[idx])
}
// If this wasn't a slice you probably shouldn't be using this function
return NewRlpValue(nil)
}
func (rlpValue *RlpValue) Cmp(o *RlpValue) bool {
return reflect.DeepEqual(rlpValue.Value, o.Value)
}
func (rlpValue *RlpValue) Encode() []byte {
return Encode(rlpValue.Value)
}
func NewRlpValueFromBytes(rlpData []byte) *RlpValue {
if len(rlpData) != 0 {
data, _ := Decode(rlpData, 0)
return NewRlpValue(data)
}
return NewRlpValue(nil)
}
// RlpValue value setters
// An empty rlp value is always a list
func EmptyRlpValue() *RlpValue {
return NewRlpValue([]interface{}{})
}
func (rlpValue *RlpValue) AppendList() *RlpValue {
list := EmptyRlpValue()
rlpValue.Value = append(rlpValue.AsSlice(), list)
return list
}
func (rlpValue *RlpValue) Append(v interface{}) *RlpValue {
rlpValue.Value = append(rlpValue.AsSlice(), v)
return rlpValue
}
/*
func FromBin(data []byte) uint64 {
if len(data) == 0 {
return 0
}
return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
}
*/
const (
RlpEmptyList = 0x80
RlpEmptyStr = 0x40
)
func Char(c []byte) int {
if len(c) > 0 {
return int(c[0])
}
return 0
}
func DecodeWithReader(reader *bytes.Buffer) interface{} {
var slice []interface{}
// Read the next byte
char := Char(reader.Next(1))
switch {
case char == 0:
return nil
case char <= 0x7c:
return char
case char <= 0xb7:
return reader.Next(int(char - 0x80))
case char <= 0xbf:
buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
length := ReadVarint(buff)
return reader.Next(int(length))
case char <= 0xf7:
length := int(char - 0xc0)
for i := 0; i < length; i++ {
obj := DecodeWithReader(reader)
if obj != nil {
slice = append(slice, obj)
} else {
break
}
}
return slice
}
return slice
}
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
/*
if pos > uint64(len(data)-1) {
log.Println(data)
log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data))
}
*/
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
//b := BigD(data[pos+1 : pos+1+l]).Uint64()
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
default:
panic(fmt.Sprintf("byte not supported: %q", char))
}
return slice, 0
}
var (
directRlp = big.NewInt(0x7f)
numberRlp = big.NewInt(0xb7)
zeroRlp = big.NewInt(0x0)
)
func Encode(object interface{}) []byte {
var buff bytes.Buffer
if object != nil {
switch t := object.(type) {
case *RlpValue:
buff.Write(Encode(t.AsRaw()))
// Code dup :-/
case int:
buff.Write(Encode(big.NewInt(int64(t))))
case uint:
buff.Write(Encode(big.NewInt(int64(t))))
case int8:
buff.Write(Encode(big.NewInt(int64(t))))
case int16:
buff.Write(Encode(big.NewInt(int64(t))))
case int32:
buff.Write(Encode(big.NewInt(int64(t))))
case int64:
buff.Write(Encode(big.NewInt(t)))
case uint16:
buff.Write(Encode(big.NewInt(int64(t))))
case uint32:
buff.Write(Encode(big.NewInt(int64(t))))
case uint64:
buff.Write(Encode(big.NewInt(int64(t))))
case byte:
buff.Write(Encode(big.NewInt(int64(t))))
case *big.Int:
buff.Write(Encode(t.Bytes()))
case []byte:
if len(t) == 1 && t[0] <= 0x7f {
buff.Write(t)
} else if len(t) < 56 {
buff.WriteByte(byte(len(t) + 0x80))
buff.Write(t)
} else {
b := big.NewInt(int64(len(t)))
buff.WriteByte(byte(len(b.Bytes()) + 0xb7))
buff.Write(b.Bytes())
buff.Write(t)
}
case string:
buff.Write(Encode([]byte(t)))
case []interface{}:
// Inline function for writing the slice header
WriteSliceHeader := func(length int) {
if length < 56 {
buff.WriteByte(byte(length + 0xc0))
} else {
b := big.NewInt(int64(length))
buff.WriteByte(byte(len(b.Bytes()) + 0xf7))
buff.Write(b.Bytes())
}
}
var b bytes.Buffer
for _, val := range t {
b.Write(Encode(val))
}
WriteSliceHeader(len(b.Bytes()))
buff.Write(b.Bytes())
}
} else {
// Empty list for nil
buff.WriteByte(0xc0)
}
return buff.Bytes()
}