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


            




                                        

 
                        
                      
 
 
                                  
                                
 
                      

                                                                   
                  





                                                                                  
                              


                                                                
                                                    


                                            


                                                            
 
                


                                               










                                                             


                                                   


                                                  
 
                            


                                                 


                                                  
 
                 


                                                


                                                  
 
                              



                                                              




                                                         
 

                                                  
 

                                                                               


                        
                           
 
 
                                                



                                     
 
                      


                                                       
                                                        


               
                                    


                        
 
                                      


                                                      


                                        
 


                                        
 

                                        
 
                    


                                            



                                                      
 
                           


                                  


                           
 
                                                                          

 
                                                      























































                                                                                                       


                                        































































                                                                      
 
package main

import (
    "bytes"
    "fmt"
    "github.com/ethereum/ethutil-go"
    "math"
    "math/big"
)

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