aboutsummaryrefslogblamecommitdiffstats
path: root/ethutil/rlp.go
blob: 1fff2b28a6a5b7f81037e0d0595290454ab05daf (plain) (tree)
1
2
3
4
5
6
7
8
9



               
             
                  

 
                          
                          



                                
                                

 



                             



                                    












                                                                 




                           

                 




                                
                     







                                                         
                          





                                                    
                                                                   






                                                       
                                                  


                            
                          


                                                                   
                                                  
                 

                            
                
                                                                  

         
                    












                                           

                                                   

                                                       





















                                                                





                                                                           

                                                     







































                                                                                   































































                                                                               
package ethutil

import (
    "bytes"
    "fmt"
    "math/big"
)

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 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
            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())
        }
    } 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
}