为什么要写这么一个编解码
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. @H_301_334@slice
由多个类型ID拼接而成,目前为了适应[]interface{}的类型,还对每一个element也进行了类型+值的编码,可以考虑非[]interface{}类型,则只在头部编码element类型即可
@H_301_334@sliceid elementid len elementid elementvalue elementid elementvalue ...
3. @H_301_334@map
map的类型ID和slice差不多
@H_301_334@mapid keyid eleid len keyid keyvalue eleid elevalue ...
4.@H_301_334@array
@H_301_334@arrayid keyid len eleid elevalue ...
5.多层嵌套类型
则是elementid又是一个slice之类 按照上面的模式先嵌套玩类型即可如:
@H_301_334@sliceid sliceid elementid [][]int
@H_301_334@mapid elementid sliceid elementid map[int][]int
对于array,len是属于type的一部分,因此在嵌套的时候会现有len
@H_301_334@arrayid arrayid elementid len len
@H_301_334@sliceid arrayid elmid len len
解码和创建
在解析出来类型id之后,通过一个预定义的map映射到reflect.type上,然后通过reflect.new函数来创建对应的类型的值,最终通过reflect.Value.interface返回为