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


               
             
                  
                 

 
                          
                          



                                
                                

 



                             



                                    












                                                                 




                           

                 




                                
                     







                                                         
                          





                                                    
                                                                   






                                                       
                                                  


                            
                          


                                                                   
                                                  
                 

                            
                
                                                                  

         
                    







                                    







                                   




                                           
                            
                                                 

                                                       





















                                                                
                                                                               




                                                             

                                                     































                                                                                   














                                                                               






                                                                                   


                                                 







                                     































































                                                                               
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.Val))
        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
}