271 lines
6.0 KiB
Go
271 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"bytes"
|
|
"math"
|
|
"math/big"
|
|
"github.com/ethereum/ethutil-go"
|
|
)
|
|
|
|
type RlpEncoder struct {
|
|
rlpData []byte
|
|
}
|
|
func NewRlpEncoder() *RlpEncoder {
|
|
encoder := &RlpEncoder{}
|
|
|
|
return encoder
|
|
}
|
|
func (coder *RlpEncoder) EncodeData(rlpData []interface{}) []byte {
|
|
return nil
|
|
}
|
|
|
|
// Data attributes are returned by the rlp decoder. The data attributes represents
|
|
// one item within the rlp data structure. It's responsible for all the casting
|
|
// It always returns something valid
|
|
type RlpDataAttribute struct {
|
|
dataAttrib interface{}
|
|
}
|
|
|
|
func NewRlpDataAttribute(attrib interface{}) *RlpDataAttribute {
|
|
return &RlpDataAttribute{dataAttrib: attrib}
|
|
}
|
|
|
|
func (attr *RlpDataAttribute) Length() int {
|
|
if data, ok := attr.dataAttrib.([]interface{}); ok {
|
|
return len(data)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (attr *RlpDataAttribute) AsUint() uint64 {
|
|
if value, ok := attr.dataAttrib.(uint8); ok {
|
|
return uint64(value)
|
|
} else if value, ok := attr.dataAttrib.(uint16); ok {
|
|
return uint64(value)
|
|
} else if value, ok := attr.dataAttrib.(uint32); ok {
|
|
return uint64(value)
|
|
} else if value, ok := attr.dataAttrib.(uint64); ok {
|
|
return value
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (attr *RlpDataAttribute) AsBigInt() *big.Int {
|
|
if a, ok := attr.dataAttrib.([]byte); ok {
|
|
return ethutil.Big(string(a))
|
|
}
|
|
|
|
return big.NewInt(0)
|
|
}
|
|
|
|
func (attr *RlpDataAttribute) AsString() string {
|
|
if a, ok := attr.dataAttrib.([]byte); ok {
|
|
return string(a)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (attr *RlpDataAttribute) AsBytes() []byte {
|
|
if a, ok := attr.dataAttrib.([]byte); ok {
|
|
return a
|
|
}
|
|
|
|
return make([]byte, 0)
|
|
}
|
|
|
|
// Threat the attribute as a slice
|
|
func (attr *RlpDataAttribute) Get(idx int) *RlpDataAttribute {
|
|
if d, ok := attr.dataAttrib.([]interface{}); ok {
|
|
// Guard for oob
|
|
if len(d) < idx {
|
|
return NewRlpDataAttribute(nil)
|
|
}
|
|
|
|
return NewRlpDataAttribute(d[idx])
|
|
}
|
|
|
|
// If this wasn't a slice you probably shouldn't be using this function
|
|
return NewRlpDataAttribute(nil)
|
|
}
|
|
|
|
type RlpDecoder struct {
|
|
rlpData interface{}
|
|
}
|
|
func NewRlpDecoder(rlpData []byte) *RlpDecoder {
|
|
decoder := &RlpDecoder{}
|
|
// Decode the data
|
|
data, _ := Decode(rlpData,0)
|
|
decoder.rlpData = data
|
|
|
|
return decoder
|
|
}
|
|
|
|
func (dec *RlpDecoder) Get(idx int) *RlpDataAttribute {
|
|
return NewRlpDataAttribute(dec.rlpData).Get(idx)
|
|
}
|
|
|
|
/// Raw methods
|
|
func BinaryLength(n uint64) uint64 {
|
|
if n == 0 { return 0 }
|
|
|
|
return 1 + BinaryLength(n / 256)
|
|
}
|
|
|
|
func ToBinarySlice(n uint64, length uint64) []uint64 {
|
|
if length == 0 {
|
|
length = BinaryLength(n)
|
|
}
|
|
|
|
if n == 0 { return make([]uint64, 1) }
|
|
|
|
slice := ToBinarySlice(n / 256, 0)
|
|
slice = append(slice, n % 256)
|
|
|
|
return slice
|
|
}
|
|
|
|
func ToBin(n uint64, length uint64) string {
|
|
var buf bytes.Buffer
|
|
for _, val := range ToBinarySlice(n, length) {
|
|
buf.WriteString(string(val))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func FromBin(data []byte) uint64 {
|
|
if len(data) == 0 { return 0 }
|
|
|
|
return FromBin(data[:len(data)-1]) * 256 + uint64(data[len(data)-1])
|
|
}
|
|
|
|
func Decode(data []byte, pos int) (interface{}, int) {
|
|
if pos > len(data)-1 {
|
|
panic(fmt.Sprintf("index out of range %d for data %q, l = %d", pos, data, len(data)))
|
|
}
|
|
|
|
char := int(data[pos])
|
|
slice := make([]interface{}, 0)
|
|
switch {
|
|
case char < 24:
|
|
return data[pos], pos + 1
|
|
|
|
case char < 56:
|
|
b := int(data[pos]) - 23
|
|
return FromBin(data[pos+1 : pos+1+b]), pos + 1 + b
|
|
|
|
case char < 64:
|
|
b := int(data[pos]) - 55
|
|
b2 := int(FromBin(data[pos+1 : pos+1+b]))
|
|
return FromBin(data[pos+1+b : pos+1+b+b2]), pos+1+b+b2
|
|
|
|
case char < 120:
|
|
b := int(data[pos]) - 64
|
|
return data[pos+1:pos+1+b], pos+1+b
|
|
|
|
case char < 128:
|
|
b := int(data[pos]) - 119
|
|
b2 := int(FromBin(data[pos+1 : pos+1+b]))
|
|
return data[pos+1+b : pos+1+b+b2], pos+1+b+b2
|
|
|
|
case char < 184:
|
|
b := int(data[pos]) - 128
|
|
pos++
|
|
for i := 0; i < b; i++ {
|
|
var obj interface{}
|
|
|
|
obj, pos = Decode(data, pos)
|
|
slice = append(slice, obj)
|
|
}
|
|
return slice, pos
|
|
|
|
case char < 192:
|
|
b := int(data[pos]) - 183
|
|
//b2 := int(FromBin(data[pos+1 : pos+1+b])) (ref implementation has an unused variable)
|
|
pos = pos+1+b
|
|
for i := 0; i < b; i++ {
|
|
var obj interface{}
|
|
|
|
obj, pos = Decode(data, pos)
|
|
slice = append(slice, obj)
|
|
}
|
|
return slice, pos
|
|
|
|
default:
|
|
panic(fmt.Sprintf("byte not supported: %q", char))
|
|
}
|
|
|
|
return slice, 0
|
|
}
|
|
|
|
func Encode(object interface{}) []byte {
|
|
var buff bytes.Buffer
|
|
|
|
switch t := object.(type) {
|
|
case uint32, uint64:
|
|
var num uint64
|
|
if _num, ok := t.(uint64); ok {
|
|
num = _num
|
|
} else if _num, ok := t.(uint32); ok {
|
|
num = uint64(_num)
|
|
}
|
|
|
|
if num >= 0 && num < 24 {
|
|
buff.WriteString(string(num))
|
|
} else if num <= uint64(math.Pow(2, 256)) {
|
|
b := ToBin(num, 0)
|
|
buff.WriteString(string(len(b) + 23) + b)
|
|
} else {
|
|
b := ToBin(num, 0)
|
|
b2 := ToBin(uint64(len(b)), 0)
|
|
buff.WriteString(string(len(b2) + 55) + b2 + b)
|
|
}
|
|
|
|
case *big.Int:
|
|
buff.Write(Encode(t.String()))
|
|
|
|
case string:
|
|
if len(t) < 56 {
|
|
buff.WriteString(string(len(t) + 64) + t)
|
|
} else {
|
|
b2 := ToBin(uint64(len(t)), 0)
|
|
buff.WriteString(string(len(b2) + 119) + b2 + t)
|
|
}
|
|
|
|
case []byte:
|
|
// Cast the byte slice to a string
|
|
buff.Write(Encode(string(t)))
|
|
|
|
case []interface{}, []string:
|
|
// Inline function for writing the slice header
|
|
WriteSliceHeader := func(length int) {
|
|
if length < 56 {
|
|
buff.WriteByte(byte(length + 128))
|
|
} else {
|
|
b2 := ToBin(uint64(length), 0)
|
|
buff.WriteByte(byte(len(b2) + 183))
|
|
buff.WriteString(b2)
|
|
}
|
|
}
|
|
|
|
// FIXME How can I do this "better"?
|
|
if interSlice, ok := t.([]interface{}); ok {
|
|
WriteSliceHeader(len(interSlice))
|
|
for _, val := range interSlice {
|
|
buff.Write(Encode(val))
|
|
}
|
|
} else if stringSlice, ok := t.([]string); ok {
|
|
WriteSliceHeader(len(stringSlice))
|
|
for _, val := range stringSlice {
|
|
buff.Write(Encode(val))
|
|
}
|
|
}
|
|
}
|
|
|
|
return buff.Bytes()
|
|
}
|