您的位置:首页 > 编程语言 > Go语言

go 自己实现一个gob编解码

2017-03-10 16:56 351 查看
代码库

为什么要写这么一个编解码

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, "哈哈,这都可以?", 1.5, -100})
a = append(a, &gob.T2{gob.T1{10, "哈哈,这都可以?", 1.5, -100}, "那么这样还可以吗?"})
a = append(a, gob.T1{10, "哈哈,这都可以?", 1.5, -100})
a = append(a, gob.T2{gob.T1{10, "哈哈,这都可以?", 1.5, -100}, "那么这样还可以吗?"})
a = append(a, 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, 0, 2)
s = append(s, "这是slice的元素")
a = append(a, s)
str := "这是一个[]byte"
s1 := []byte(str)
a = append(a, s1)

b := make([]interface{}, 0, 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(r), 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返回为
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息