plugeth/common/rlp.go

293 lines
6.1 KiB
Go
Raw Normal View History

2015-07-07 00:54:22 +00:00
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum 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.
//
// go-ethereum 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 go-ethereum. If not, see <http://www.gnu.org/licenses/>.
2015-03-16 10:27:38 +00:00
package common
2014-02-14 22:56:09 +00:00
import (
"bytes"
"fmt"
"math/big"
2014-12-17 11:57:35 +00:00
"reflect"
2014-02-14 22:56:09 +00:00
)
2014-08-11 14:23:38 +00:00
type RlpEncode interface {
2014-03-20 16:27:26 +00:00
RlpEncode() []byte
2014-08-11 14:23:38 +00:00
}
type RlpEncodeDecode interface {
RlpEncode
2014-06-15 22:52:10 +00:00
RlpValue() []interface{}
2014-03-20 16:27:26 +00:00
}
type RlpEncodable interface {
RlpData() interface{}
}
2014-09-19 11:19:19 +00:00
func Rlp(encoder RlpEncode) []byte {
return encoder.RlpEncode()
}
2014-02-14 22:56:09 +00:00
type RlpEncoder struct {
rlpData []byte
}
func NewRlpEncoder() *RlpEncoder {
encoder := &RlpEncoder{}
return encoder
}
func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
return Encode(rlpData)
}
const (
RlpEmptyList = 0x80
RlpEmptyStr = 0x40
)
2014-08-01 08:21:43 +00:00
const rlpEof = -1
2014-02-14 22:56:09 +00:00
func Char(c []byte) int {
if len(c) > 0 {
return int(c[0])
}
2014-08-01 08:21:43 +00:00
return rlpEof
2014-02-14 22:56:09 +00:00
}
func DecodeWithReader(reader *bytes.Buffer) interface{} {
var slice []interface{}
// Read the next byte
char := Char(reader.Next(1))
switch {
2014-03-27 14:38:55 +00:00
case char <= 0x7f:
2014-02-14 22:56:09 +00:00
return char
case char <= 0xb7:
return reader.Next(int(char - 0x80))
case char <= 0xbf:
length := ReadVarInt(reader.Next(int(char - 0xb7)))
2014-02-14 22:56:09 +00:00
return reader.Next(int(length))
case char <= 0xf7:
length := int(char - 0xc0)
for i := 0; i < length; i++ {
obj := DecodeWithReader(reader)
2014-08-01 08:21:43 +00:00
slice = append(slice, obj)
2014-02-14 22:56:09 +00:00
}
return slice
case char <= 0xff:
length := ReadVarInt(reader.Next(int(char - 0xf7)))
for i := uint64(0); i < length; i++ {
obj := DecodeWithReader(reader)
2014-08-01 08:21:43 +00:00
slice = append(slice, obj)
2014-02-14 22:56:09 +00:00
}
2014-08-01 08:21:43 +00:00
return slice
2014-02-14 22:56:09 +00:00
default:
2014-08-01 08:21:43 +00:00
panic(fmt.Sprintf("byte not supported: %q", char))
2014-02-14 22:56:09 +00:00
}
return slice
2014-02-14 22:56:09 +00:00
}
var (
directRlp = big.NewInt(0x7f)
numberRlp = big.NewInt(0xb7)
zeroRlp = big.NewInt(0x0)
)
2014-12-17 11:57:35 +00:00
func intlen(i int64) (length int) {
for i > 0 {
i = i >> 8
length++
}
return
}
2014-02-14 22:56:09 +00:00
func Encode(object interface{}) []byte {
var buff bytes.Buffer
if object != nil {
switch t := object.(type) {
2014-02-15 00:34:18 +00:00
case *Value:
buff.Write(Encode(t.Val))
case RlpEncodable:
buff.Write(Encode(t.RlpData()))
2014-02-14 22:56:09 +00:00
// 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:
// Not sure how this is possible while we check for nil
2014-04-09 16:27:25 +00:00
if t == nil {
buff.WriteByte(0xc0)
} else {
buff.Write(Encode(t.Bytes()))
}
2014-09-15 13:42:12 +00:00
case Bytes:
buff.Write(Encode([]byte(t)))
2014-02-14 22:56:09 +00:00
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())
2014-12-17 11:57:35 +00:00
default:
// This is how it should have been from the start
// needs refactoring (@fjl)
v := reflect.ValueOf(t)
switch v.Kind() {
case reflect.Slice:
var b bytes.Buffer
for i := 0; i < v.Len(); i++ {
b.Write(Encode(v.Index(i).Interface()))
}
blen := b.Len()
if blen < 56 {
buff.WriteByte(byte(blen) + 0xc0)
} else {
2014-12-18 10:39:24 +00:00
ilen := byte(intlen(int64(blen)))
buff.WriteByte(ilen + 0xf7)
t := make([]byte, ilen)
for i := byte(0); i < ilen; i++ {
t[ilen-i-1] = byte(blen >> (i * 8))
}
buff.Write(t)
2014-12-17 11:57:35 +00:00
}
buff.ReadFrom(&b)
}
2014-02-14 22:56:09 +00:00
}
} else {
// Empty list for nil
buff.WriteByte(0xc0)
}
return buff.Bytes()
}
// 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) {
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(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 := ReadVarInt(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
}