aboutsummaryrefslogtreecommitdiffstats
path: root/common/rlp.go
diff options
context:
space:
mode:
Diffstat (limited to 'common/rlp.go')
-rw-r--r--common/rlp.go276
1 files changed, 276 insertions, 0 deletions
diff --git a/common/rlp.go b/common/rlp.go
new file mode 100644
index 000000000..602f13202
--- /dev/null
+++ b/common/rlp.go
@@ -0,0 +1,276 @@
+package common
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "reflect"
+)
+
+type RlpEncode interface {
+ RlpEncode() []byte
+}
+
+type RlpEncodeDecode interface {
+ RlpEncode
+ RlpValue() []interface{}
+}
+
+type RlpEncodable interface {
+ RlpData() interface{}
+}
+
+func Rlp(encoder RlpEncode) []byte {
+ return encoder.RlpEncode()
+}
+
+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
+)
+
+const rlpEof = -1
+
+func Char(c []byte) int {
+ if len(c) > 0 {
+ return int(c[0])
+ }
+
+ return rlpEof
+}
+
+func DecodeWithReader(reader *bytes.Buffer) interface{} {
+ var slice []interface{}
+
+ // Read the next byte
+ char := Char(reader.Next(1))
+ switch {
+ case char <= 0x7f:
+ return char
+
+ case char <= 0xb7:
+ return reader.Next(int(char - 0x80))
+
+ case char <= 0xbf:
+ length := ReadVarInt(reader.Next(int(char - 0xb7)))
+
+ return reader.Next(int(length))
+
+ case char <= 0xf7:
+ length := int(char - 0xc0)
+ for i := 0; i < length; i++ {
+ obj := DecodeWithReader(reader)
+ slice = append(slice, obj)
+ }
+
+ return slice
+ case char <= 0xff:
+ length := ReadVarInt(reader.Next(int(char - 0xf7)))
+ for i := uint64(0); i < length; i++ {
+ obj := DecodeWithReader(reader)
+ slice = append(slice, obj)
+ }
+
+ return slice
+ default:
+ panic(fmt.Sprintf("byte not supported: %q", char))
+ }
+
+ return slice
+}
+
+var (
+ directRlp = big.NewInt(0x7f)
+ numberRlp = big.NewInt(0xb7)
+ zeroRlp = big.NewInt(0x0)
+)
+
+func intlen(i int64) (length int) {
+ for i > 0 {
+ i = i >> 8
+ length++
+ }
+ return
+}
+
+func Encode(object interface{}) []byte {
+ var buff bytes.Buffer
+
+ if object != nil {
+ switch t := object.(type) {
+ case *Value:
+ buff.Write(Encode(t.Raw()))
+ case RlpEncodable:
+ buff.Write(Encode(t.RlpData()))
+ // 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
+ if t == nil {
+ buff.WriteByte(0xc0)
+ } else {
+ buff.Write(Encode(t.Bytes()))
+ }
+ case Bytes:
+ buff.Write(Encode([]byte(t)))
+ 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())
+ 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 {
+ 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)
+ }
+ buff.ReadFrom(&b)
+ }
+ }
+ } 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
+}