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


            
       



         

                                    
 





                                                      

   






















                                                                      
                                                      



                                                                                         
                        
                                 

                 
                             
 


                                                      
 



                                                          
 


                                       
 



                                                 
 









                                  
 

                             
                                                                                           










                                                      
   

                 





                                        










                                               





                                                     
     








                                                      

                                      
                                 




                                                   
                                          

                                      

                                           














                                                   



                     
package main

import (
  "fmt"
  "bytes"
  "math"
)

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