package gob

import “encoding/gob”

gob包管理gob流——在编码器(发送器)和解码器(接受器)之间交换的binary值。一般用于传递远端程序调用(RPC)的参数和结果,如net/rpc包就有提供。

本实现给每一个数据类型都编译生成一个编解码程序,当单个编码器用于传递数据流时,会分期偿还编译的消耗,是效率最高的。

Basics

Gob流是自解码的。流中的所有数据都有前缀(采用一个预定义类型的集合)指明其类型。指针不会传递,而是传递值;也就是说数据是压平了的。递归的类型可以很好的工作,但是递归的值(比如说值内某个成员直接/间接指向该值)会出问题。这个问题将来可能会修复。

要使用gob,先要创建一个编码器,并向其一共一系列数据:可以是值,也可以是指向实际存在数据的指针。编码器会确保所有必要的类型信息都被发送。在接收端,解码器从编码数据流中恢复数据并将它们填写进本地变量里。

Types and Values

发送端和接收端的值/类型不需要严格匹配。对结构体来说,字段(根据字段名识别)如果发送端有而接收端没有,会被忽略;接收端有而发送端没有的字段也会被忽略;发送端和接收端都有的字段其类型必须是可兼容的;发送端和接收端都会在gob流和实际go类型之间进行必要的指针取址/寻址工作。举例如下:

struct { A, B int } 可以和如下类型互相发送和接收:

struct { A, B int } // 同一类型 *struct { A, B int } // 结构体需要额外重定向(指针) struct { *A, **B int } // 字段需要额外重定向(指针) struct { A, B int64 } // 同为整型/浮点型且符号类型相同的不同值类型,参见下面 可以发送给如下任一类型:

struct { A, B int } // 同一类型 struct { B, A int } // 字段顺序改变无影响,按名称匹配 struct { A, B, C int } // 忽略多出的字段C struct { B int } // 忽略缺少的字段A,会丢弃A的值 struct { B, C int } // 忽略缺少的字段A,忽略多出的字段C 但尝试发送给如下类型的话就会导致错误:

struct { A int; B uint } // B字段改变了符号类型 struct { A int; B float } // B字段改变了类型 struct { } // 无共同字段名 struct { C, D int } // 无共同字段名 整数以两种方式传递:任意精度有符号整数和任意精度无符号整数。Gob里只有无符号和有符号整数的区别,没有int8、int16等类型的区分。如下所述,发送端会以变长编码发送整数值;接收端接收整数并保存在目标变量里。浮点数则总是使用IEEE-754 64位精度(参见下述)。

有符号整数可以被任意有符号整形接收:int、int16等;无符号整数可以被任意无符号整形接收;浮点数可以被任意浮点数类型接收。但是,接收端类型必须能容纳该值(上溢/下溢都不行),否则解码操作会失败。

结构体、数组和切片都被支持。结构体只编码和解码导出字段。字符串和byte数组/切片有专门的高效表示(参见下述)。当解码切片时,如果当前切片的容量足够会被复用,否则会申请新的底层数组(所以还是用切片地址为好)。此外,生成的切片的长度会修改为解码的成员的个数。

Gob流不支持函数和通道。试图在最顶层编码这些类型的值会导致失败。结构体中包含函数或者通道类型的字段的话,会视作非导出字段(忽略)处理。

Gob可以编码任意实现了GobEncoder接口或者encoding.BinaryMarshaler接口的类型的值(通过调用对应的方法),GobEncoder接口优先。

Gob可以解码任意实现了GobDecoder接口或者encoding.BinaryUnmarshaler接口的类型的值(通过调用对应的方法),同样GobDecoder接口优先。

Encoding Details

这部分文档是编码细节,对多数使用者并不重要。细节是按从底向上的顺序展示的。

无符号整数用两种方法发送。如果该整数小于128,则以一个字节发送该值;否则采用最小长度大端在前的字节流保存该整数,并在最前面使用一个字节保存字节流的字节数相反数。因此0被发送为(00),7被发送为(07),而256被发送为(FE 01 00)(字节数2,其相反数-2,用补码表示,为FE)。

布尔值按无符号整数编码,0表示假,1表示真。

有符号整数翻译为一个无符号整数(i=>u)来编码。u的最低字位表示值的符号(以及是否应对值按位取反);其余位表示值。编码算法表示为如下(非实际代码):

uint u; if i < 0 { u = (^i << 1) | 1 // i按位取反,左移1位,第1位设为1 } else { u = (i << 1) // i不进行取反,左移1位,第1位为0 } encodeUnsigned(u) 这样一来,最低位就相当于标志位,但还会对负数按位取反,以便保证负数不会出现特殊情况(补码表示的负数,其表示会受到整数类型的影响)。如,-129=^128=(^256>>1)编码为(FE 01 01)。

浮点数的数值,首先总是转换float64类型值,该值使用math.Float64bits 函数转换为一个uint64整数,然后将字节序颠倒过来,最后作为一个普通无符号数编码传输。颠倒字节序说明数字的指数和高精度位数部分首先传送。因为低位一般是0,这样可以节约编码的字节数。例如,17.0编码后只有三个字节(FE 31 40)。

字符串和byte数组/切片发送为一个无符号整数指定的字节数后跟不作处理的字节序列。

其它切片和数组都发送为一个无符号整数指定的成员个数后跟所有成员的递归表示的gob编码。

映射发送为一个无符号整数指定的键值对数后给许多键和值的gob编码。非nil但无成员的映射也会发送,因此如果发送者申请了一个映射,接收方也会申请一个映射,即使该映射内没有元素。

结构体也以键值对(字段名:字段值)序列的形式发送。字段值采用递归表示的gob编码发送。如果字段为其类型的零值,则该字段不会被发送。字段编号由编码的结构体的类型确定:编码类型的第一个字段为字段0,第二个为字段1,依次类推。当编码一个结构体的值时,字段编号出于效率考虑是增量编码的,字段总是按字段编号递增的顺序被编码,增量是无符号整数。增量编码将字段编号初始化为-1,因此无符号整型值为7的字段0编码为增量1值7。最后,所有的字段都被发送后,会发送终止标记表明结构体的结束。终止标记为一个增量为0的值,其表示为(00)。

接口类型不会检查兼容性;所有的接口都被视为同一种“接口”类型来传输;类似整数和字节切片,它们都被视为interface{}类型。接口值发送为一个表示其具体类型的字符串标志符(必须由调用者预先注册的名称)后跟表示后续数据字节数的无符号整数(以便需要时可以跳过该值),再后跟保存在接口里的值的动态具体类型的gob编码。nil接口值会发送为标志符为空字符串、不发送值的接口。在接收到之后,由解码器验证该值是否满足接收端变量接口。

类型的表示如下所示。当一个编码器和解码器的连接中定义了一个类型时,该类型会被指定一个整数类型ID。当调用Encoder.Encode(v)时,该方法会确保v及v所有成员都有对应ID,然后本方法会发送一系列对(typeid,encoded-v) ,其中typeid是编码类型的类型ID,encoded-v是值v的gob编码。

为了定义一个类型,编码器会选择一个未使用的正数作为id并发送对(-type id, encoded-type),其中encoded-type 是由如下类型构成、描述该类型的wireType类型的gob编码。

type wireType struct { ArrayT *ArrayType SliceT *SliceType StructT *StructType MapT *MapType } type arrayType struct { CommonType Elem typeId Len int } type CommonType struct { Name string // 结构体的名字 Id int // 类型的ID } type sliceType struct { CommonType Elem typeId } type structType struct { CommonType Field []*fieldType // 结构体的字段 } type fieldType struct { Name string // 字段名 Id int // 字段的类型ID,必须是已经定义的类型 } type mapType struct { CommonType Key typeId Elem typeId } 如果有嵌套的类型,必须先定义所有内部类型的ID才能定义顶层类型的ID用于描述encoded-v。

为了简化启动,有些类型是预先定义好了的,这些类型及其ID如下:

bool 1 int 2 uint 3 float 4 []byte 5 string 6 complex 7 interface 8 // 空缺用于保留ID WireType 16 ArrayType 17 CommonType 18 SliceType 19 StructType 20 FieldType 21 // 22是[]fieldType类型的ID MapType 23 最后,每一次调用Encode创建的信息都会以一个编码了的无符号整数指明消息剩余部分的字节数。在开始的类型名后面,接口值也以同样的方式包装;事实上,接口值表现的就像对Encode进行递归调用一样。

总的来说,一个gob流看起来是这样的:

(byteCount (-type id, encoding of a wireType)* (type id, encoding of a value))* 其中*表示0或多次重复,值的类型id必须是预定义的,或者在流中值的前面定义。

参见”Gobs of data”获取gob wire格式的设计讨论:

http://golang.org/doc/articles/gobs_of_data.html

Example (Basic) Example (EncodeDecode) Example (Interface) Index 返回首页

type GobDecoder type GobEncoder func Register(value interface{}) func RegisterName(name string, value interface{}) type Decoder func NewDecoder(r io.Reader) *Decoder func (dec *Decoder) Decode(e interface{}) error func (dec *Decoder) DecodeValue(v reflect.Value) error type Encoder func NewEncoder(w io.Writer) *Encoder func (enc *Encoder) Encode(e interface{}) error func (enc *Encoder) EncodeValue(value reflect.Value) error type CommonType Examples

返回首页

package (Basic) package (EncodeDecode) package (Interface) type GobDecoder type GobDecoder interface { // GobDecode必须是指针的方法,使用切片数据重写指针指向的值 // 切片数据应该由同一具体类型的GobEncode生成 GobDecode([]byte) error } GobDecoder是一个描述数据的接口,提供自己的方案来解码GobEncoder发送的数据。

type GobEncoder type GobEncoder interface { // GobEncode方法返回一个代表值的切片数据 // 该切片数据由同一类型的指针方法GobDecoder接受解码 GobEncode() ([]byte, error) } GobEncoder是一个描述数据的接口,提供自己的方案来将数据编码供GobDecoder接收并解码。一个实现了GobEncoder接口和GobDecoder接口的类型可以完全控制自身数据的表示,因此可以包含非导出字段、通道、函数等数据,这些数据gob流正常是不能传输的。

注意:因为gob数据可以被永久保存,在软件更新的过程中保证用于GobEncoder编码的数据的稳定性(保证兼容)是较好的设计原则。例如,让GobEncode 接口在编码后数据里包含版本信息可能很有意义。

func Register func Register(value interface{}) Register记录value下层具体值的类型和其名称。该名称将用来识别发送或接受接口类型值时下层的具体类型。本函数只应在初始化时调用,如果类型和名字的映射不是一一对应的,会panic。

func RegisterName func RegisterName(name string, value interface{}) RegisterName类似Register,淡灰使用提供的name代替类型的默认名称。

type Decoder type Decoder struct { // 内含隐藏或不导出字段 } Decoder管理从连接远端读取的类型和数据信息的解释(的操作)。

func NewDecoder

func NewDecoder(r io.Reader) Decoder 函数返回一个从r读取数据的Decoder,如果r不满足io.ByteReader接口,则会包装r为bufio.Reader。

func (*Decoder) Decode

func (dec *Decoder) Decode(e interface{}) error Decode从输入流读取下一个之并将该值存入e。如果e是nil,将丢弃该值;否则e必须是可接收该值的类型的指针。如果输入结束,方法会返回io.EOF并且不修改e(指向的值)。

func (*Decoder) DecodeValue

func (dec *Decoder) DecodeValue(v reflect.Value) error DecodeValue从输入流读取下一个值,如果v是reflect.Value类型的零值(v.Kind() == Invalid),方法丢弃该值;否则它会把该值存入v。此时,v必须代表一个非nil的指向实际存在值的指针或者可写入的reflect.Value(v.CanSet()为真)。如果输入结束,方法会返回io.EOF并且不修改e(指向的值)。

type Encoder type Encoder struct { // 内含隐藏或不导出字段 } Encoder管理将数据和类型信息编码后发送到连接远端(的操作)。

func NewEncoder

func NewEncoder(w io.Writer) Encoder NewEncoder返回一个将编码后数据写入w的Encoder。

func (*Encoder) Encode

func (enc *Encoder) Encode(e interface{}) error Encode方法将e编码后发送,并且会保证所有的类型信息都先发送。

func (*Encoder) EncodeValue

func (enc *Encoder) EncodeValue(value reflect.Value) error EncodeValue方法将value代表的数据编码后发送,并且会保证所有的类型信息都先发送。

type CommonType type CommonType struct { Name string Id typeId } CommonType保存所有类型的成员。这是个历史遗留“问题”,出于保持binary兼容性才保留,只用于类型描述的编码。该类型应视为不导出类型。