为什么要写这么一个编解码
golang内部也实现了一个gob的二进制编解码,接口简单,使用方便,但是它对interface的支持不是很好,
比如如下代码:
var in []interface{} = []interface{}{1, 1.5,"abc","123",map[int]int{1: 1, 2: 2, 3: 3}}
enc.Encode(in)
var out []interface{}
dec.Decode(&out)
fmt.Println(out)
使用gob是无法对上面的in进行正确编解码的,为了解决上面的问题,所以我需要自己实现一个可以完美支持任意内置类型的interface{}的编码系统
想法
为了达到这样的目的,我们需要将每一个interface的类型ID和值编码都编码进二进制流中.
这样就可以在解析的时候,根据对应的类型ID生成值并且解析值。
因为需要自动生成类型ID的值,所以对于自定义类型我们需要进行注册。目前只支持自定义的结构体注册。
使用
package gob_test
import "github.com/sydnash/lotou/encoding/gob"
import "fmt"
import "testing"
import "reflect"
func TestType(t *testing.T) {
enc := gob.NewEncoder()
a := make([]interface{}, 0, 10)
a = append(a,int(-5))
a = append(a,int8(-1))
a = append(a,int16(-2))
a = append(a,int32(-3))
a = append(a,int64(-4))
a = append(a,uint(6))
a = append(a,uint8(7))
a = append(a,uint16(8))
a = append(a,uint32(9))
a = append(a,uint64(10))
a = append(a,float32(0.99999))
a = append(a,float64(0.9999999999))
a = append(a,"this is a string")
a = append(a,"这也是一个string")
a = append(a,&gob.T1{10,"哈哈,这都可以?", -100})
a = append(a,&gob.T2{gob.T1{10, -100},"那么这样还可以吗?"})
a = append(a,gob.T1{10,gob.T2{gob.T1{10,true)
a = append(a,false)
a = append(a,[3]int{1, 2, 3})
a = append(a,[]byte{})
m := make(map[int]string)
m[1] = "map的第一个元素"
m[1] = "map的第二个元素"
a = append(a,m)
s := make([]string, 2)
s = append(s,"这是slice的元素")
a = append(a,s)
str := "这是一个[]byte"
s1 := []byte(str)
a = append(a,s1)
b := make([]interface{}, 10)
b = append(b,m)
b = append(b,s)
b = append(b,s1)
a = append(a,b)
a = append(a,a)
//start encode
for _,v := range a {
enc.Encode(v)
}
//create decoder
dec := gob.NewDecoder()
dec.SetBuffer(enc.Buffer())
var ok bool = true
var r interface{}
idx := 0
for ok {
//decode
r,ok = dec.Decode()
fmt.Println(r,reflect.TypeOf(r),ok)
if ok {
//check decode is ok?
if !reflect.DeepEqual(r,a[idx]) {
t.Errorf("%v is not equal to %v at idx %v",r,a[idx],idx)
}
if reflect.TypeOf(r) != reflect.TypeOf(a[idx]) {
t.Errorf("%v is not equal to %v at idx %v",reflect.TypeOf(a[idx]),idx)
}
idx++
}
}
}
可以编解码任意的内置类型的slice map。
主要实现
类型编码
- 基本类型编码
类型ID由structID、指针深度、reflect.kind三部分拼接而成,
func gernerateId(kind,depth,structId uint) uint16 {
id := uint16(structId)<<8 | uint16(depth)<<5 | uint16(kind)
return id
}
其中structID在注册struct类型的时候分配
2. slice
由多个类型ID拼接而成,目前为了适应[]interface{}的类型,还对每一个element也进行了类型+值的编码,可以考虑非[]interface{}类型,则只在头部编码element类型即可
sliceid elementid len elementid elementvalue elementid elementvalue ...
3. map
map的类型ID和slice差不多
mapid keyid eleid len keyid keyvalue eleid elevalue ...
4.array
arrayid keyid len eleid elevalue ...
5.多层嵌套类型
则是elementid又是一个slice之类 按照上面的模式先嵌套玩类型即可如:
sliceid sliceid elementid
[][]int
mapid elementid sliceid elementid
map[int][]int
对于array,len是属于type的一部分,因此在嵌套的时候会现有len
arrayid arrayid elementid len len
sliceid arrayid elmid len len
解码和创建
在解析出来类型id之后,通过一个预定义的map映射到reflect.type上,然后通过reflect.new函数来创建对应的类型的值,最终通过reflect.Value.interface返回为