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. 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返回为

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1&#160;Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...