编码/ gob是否确定?

时间:2015-10-20 05:31:04

标签: go gob

我们可以期望两个Go对象x,y使得x等于y(假设没有接口和映射,只是结构和数组的技巧)gob_encode(x)和gob_encode(y)的输出将始终是一样吗?

编辑(2018年6月8日):

当涉及地图时,

gob编码非确定性。这是由于地图的随机迭代顺序,导致它们的序列化是随机排序的。

3 个答案:

答案 0 :(得分:5)

只要“完成工作”,你就不应该真正关心。但目前的encoding/gob实施是确定性的。但是(继续阅读)!

自:

  

gobs流是自我描述的。流中的每个数据项前面都有一个类型的规范,用一小组预定义类型表示。

这意味着如果您是第一次对类型的值进行编码,则会发送类型信息。如果您对另一个相同类型的值进行编码,则不会再次传输类型描述,只是对其先前规范的引用。因此,即使您对相同的值进行两次编码,它也会生成不同的字节序列,因为第一个将包含类型规范和值,第二个将仅包含类型ref(例如类型ID)和值。

见这个例子:

type Int struct{ X int }

b := &bytes.Buffer{}
e := gob.NewEncoder(b)

e.Encode(Int{1})
fmt.Println(b.Bytes())

e.Encode(Int{1})
fmt.Println(b.Bytes())

e.Encode(Int{1})
fmt.Println(b.Bytes())

输出(在Go Playground上尝试):

[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0]
[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0 5 255 130 1 2 0]
[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0 5 255 130 1 2 0 5 255 130 1 2 0]

如第一个Encode()生成大量字节加上Int值为[5 255 130 1 2 0]的值,第二次和第三次调用会添加相同的[5 255 130 1 2 0]序列。< / p>

但是如果您创建2个不同的gob.Encoder并且您以相同的顺序编写相同的值,它们将产生精确的结果。

请注意,在之前的声明中,“相同顺序”也很重要。因为在发送这种类型的第一个值时发送类型规范,所以以不同顺序发送不同类型的值也将以不同的顺序发送类型规范,因此类型的引用/标识符可能不同,这意味着当值为0时这种类型被编码,将使用/发送不同类型的引用/ id。

另请注意,gob包的实现可能会在发行版之间发生变化。这些更改将向后兼容(如果由于某种原因,它们必须明确说明它们会进行向后不兼容的更改),但向后兼容并不意味着输出是相同的。因此,不同的Go版本可能会产生不同的结果(但所有兼容版本都可以解码)。

答案 1 :(得分:1)

应该注意的是,接受的答案是不正确的:encoding / gob不会以确定的方式对地图元素进行排序:https://play.golang.org/p/Hh3_5Kb3Znn

我已经分叉编码/ gob并添加了一些代码来按键排序地图,然后再将它们写入流中。这会影响性能,但我的特定应用程序不需要高性能。请记住,自定义封送程序可以解决此问题,因此请谨慎使用:https://github.com/dave/stablegob

答案 2 :(得分:0)

使用不同的类型和不同的编码器也不确定。

示例:

package main

import (
    "bytes"
    "crypto/sha1"
    "encoding/gob"
    "encoding/hex"
    "log"
)

func main() {
    encint()
    encint64()
    encstring()

}

func encint() {
    s1 := []int{0, 2, 4, 5, 7}
    buf2 := bytes.Buffer{}
    enc2 := gob.NewEncoder(&buf2)
    enc2.Encode(s1)
}

func encint64() {
    s1 := []int64{0, 2, 4, 5, 7}
    buf2 := bytes.Buffer{}
    enc2 := gob.NewEncoder(&buf2)
    enc2.Encode(s1)
}

func encstring() {
    s1 := []string{"a", "b", "c", "d"}
    buf2 := bytes.Buffer{}
    enc2 := gob.NewEncoder(&buf2)
    enc2.Encode(s1)
    log.Println(buf2.Bytes())

    hash := sha1.New()
    hash.Write(buf2.Bytes())
    ret := hash.Sum(nil)
    log.Println(hex.EncodeToString(ret))
}

Go Playground中运行

请注意,如果您注释掉encint()encint64(),则encstring将产生不同的字节和不同的哈希码。

即使使用不同的对象/指针,也会发生这种情况。