go - 两个 gob 编码器产生不同的结果

标签 go gob

...这让我发疯,试图理解我做错了什么!

Playground :https://go.dev/play/p/ZQP8Y-gwihQ

该示例看起来很人为,但它是从我出现错误的代码中提取的。在我的代码中,我对字节缓冲区进行哈希处理,并希望该过程是可预测的。

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
    "log"
)

type Foo struct {
    Bar string
    Baz string
}

func (f *Foo) X() string {
    var b bytes.Buffer
    s := struct {
        Bar string
        Baz string
    }{
        f.Bar,
        f.Baz,
    }
    log.Printf("%v", s)
    gob.NewEncoder(&b).Encode(s)
    return fmt.Sprintf("%x", b)
}

func (f *Foo) Y(x string) string {
    var b bytes.Buffer
    s := struct {
        Bar string
        Baz string
        S   string
    }{
        f.Bar,
        f.Baz,
        x,
    }
    log.Printf("%v", s)
    gob.NewEncoder(&b).Encode(s)
    return fmt.Sprintf("%x", b)
}

func main() {
    a := &Foo{
        Bar: "bar",
        Baz: "baz",
    }

    log.Println(a.X())
    log.Println(a.Y("something"))
}

运行 yield :

{bar baz}
{1cff81030102ff820001020103426172010c00010342617a010c0000000dff820103626172010362617a00 0 0}
{bar baz something}
{22ff83030102ff840001030103426172010c00010342617a010c00010153010c00000018ff840103626172010362617a0109736f6d657468696e6700 0 0}

注释掉log.Println(a.X())会产生:

{bar baz something}
{22ff81030102ff820001030103426172010c00010342617a010c00010153010c00000018ff820103626172010362617a0109736f6d657468696e6700 0 0}

我希望这两种编码相同,但它们在我认为对应于字段边界的位置上有所不同(可以预见):

22
ff83 # 81
030102

ff84 # 82
0001030103426172010c00010342617a010c00010153010c00000018

ff84 # 82
0103626172010362617a0109736f6d657468696e6700

尽管细节有所不同,但行为与我的代码一致。

我在每个方法中创建一个新的 bytes.Buffergob.NewEncoder,因此不清楚为什么调用 X 会改变结果Y

最佳答案

您缺少的是,Encoder 实例生成的字节流除了每个 之外还具有全局(程序范围)状态编码器状态。该全局状态由[注意:此处编辑的短语]注册并发送的类型组成。

当您发送类型化值时,如果该类型在发送之前尚未注册,它将在全局状态下为您注册。这会为该类型分配一个内部数值。请参阅Register (及其同伴 RegisterName)。当您调用 X 时,会注册在 X 中保存 s 的匿名结构类型。当您调用 Y 时,会注册在 Y 中保存 s 的匿名结构类型。它们有不同的内部类型号。如果调用X,则该类型永远不会注册,并且Y的类型会在第一个可用编号下注册。

In my code I'm hashing the bytes buffer ...

这不是一个好主意,因为现在可能是显而易见的原因。 但是,如果您以已知的顺序显式注册每种类型,那么您在这里就足够安全了,除非将来的版本出于某种(可能是好的)原因更改了线路格式。 哎呀,测试表明它并没有也无济于事。这是因为,即使该类型已注册,在第一次对该类型的值进行编码之前,它也不会设置传输编号。因此,您需要对每种类型的值进行编码(并且可以选择丢弃)。

Here is a functioning example仔细丢弃编码这两种类型,以便注释掉对 log.Println(a.X()) 的调用对第二个值的编码没有影响。

关于go - 两个 gob 编码器产生不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70477328/

相关文章:

go - 使用Go 1.13在Google Cloud Function上进行“No such file or directory”

循环和 map 引用

用于通过 RPC 传递匿名函数的 GobEncoder

go - 反序列化未知的 Go 的 gob blob

go - 在不知 Prop 体类型的情况下解码 gob 输出

pointers - 如何在 Go 中使用接收器?

go - 在 ptr 值上反射(reflect) : call of reflect. Value.Field

go - 使用 gob 打包递归定义的结构体

go - 调用基本类型的方法