go 自己实现一个gob编解码

前端之家收集整理的这篇文章主要介绍了go 自己实现一个gob编解码前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

代码库

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

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。

主要实现

类型编码

  1. 基本类型编码

类型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返回为

原文链接:https://www.f2er.com/go/188822.html

猜你在找的Go相关文章