Go 语言引导

本文将介绍如何在 Go 语言中使用 bitproto 。

编译 bitproto 生成 Go 文件

首先,创建一个目录 bp 用来存放生成的 go 文件:

$ mkdir bp

然后执行 bitproto 的编译器来生成 Go 代码:

$ bitproto go pen.bitproto bp/

其中, pen.bitproto 已经在前面的章节 一个示例 bitproto 中做过说明。

我们会发现 bitproto 会为我们生成一个叫做 pen_bp.go 的文件到之前创建的目录 bp 中,其中包括结构体、常量和一些接口函数。

在生成的 pen_bp.go 中:

  • bitproto 中的 enum Color 会映射成 Go 中的使用 type 关键字定义的一个无符号整数的类型,枚举值映射成了这个类型的常量:

    type Color uint8 // 3bit
    const (
       COLOR_UNKNOWN Color = 0
       COLOR_RED = 1
       COLOR_BLUE = 2
       COLOR_GREEN = 3
    )
    
  • bitproto 中的 Timestamp 类型映射成了 Go 中的 type 关键字定义的 int64 类型:

    type Timestamp int64 // 64bit
    
  • bitproto 中的 message Pen 映射成为了 Go 中的一个结构体:

    type Pen struct {
       Color Color `json:"color"` // 3bit
       ProducedAt Timestamp `json:"produced_at"` // 64bit
    }
    
  • bitproto 的编译器同样生成了结构体上的两个方法,分别是编码函数和解码函数:

    func (m *Pen) Encode() []byte
    func (m *Pen) Decode(s []byte)
    

安装 bitproto 的 Go 支持库

bitproto 序列化需要依赖目标语言的支持库来工作,在这里,我们生成的编解码函数依赖 bitproto 的 Go 语言支持库。

bitproto 的 Go 语言的支持库存在在 Github 上,可以通过 go get 来获取:

$ go get github.com/hit9/bitproto/lib/go

如果你希望使用 go mode 把这个库安装到在本地的 vendor 目录:

$ cd bp && go mod init && go mod vendor

运行代码

现在,我们创建了一个叫做 main.go 的 Go 文件,包含以下代码:

package main

import (
     "fmt"

     bp "path/to/bp"
)

func main() {
     // Encode
     p := &bp.Pen{bp.COLOR_RED, 1611515729966}
     s := p.Encode()

     // Decode
     p1 := &bp.Pen{}
     p1.Decode(s)

     fmt.Printf("%v", p1)
}

注意替换 pen_bp.go 的导入路径。

在上面的代码中,我们首先创建了 Pen 类型的一个实例 p , 并做了数据的初始化,然后调用了一个方法 p.Encode() 来把 p 编码到字节流 s 中,其中 s 是一个字节切片。

在解码的部分,我们创建了另一个实例 p1 , 然后调用方法 p1.Decode() 来从字节流 s 中解码数据到 p1

bitproto 的编译器同样在结构体的字段上生成了 json 标签,这样方便对结构体做 json 的格式化。同样为结构体生成了一个函数叫做 String() ,它返回结构体的 json 格式字符串。

现在执行它:

$ go run main.go
{"color":1,"produced_at":1611515729966}

编码函数和解码函数会一个字节一个字节的在结构体和字节流之间拷贝比特数据。bitproto 的 Go 语言支持库没有用到任何反射 (比如 encoding/json 那样),因为反射可能会影响性能。同样,也没有用到任何类型断言和动态函数构建。

github 上有一个更大一些的例子。