两个 gob 编码器产生不同的结果
Two gob encoders produce different results
...这让我发疯试图理解我做错了什么!
游乐场: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"))
}
运行 产量:
{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.Buffer
和 gob.NewEncoder
,因此不清楚为什么调用 X
会更改 Y
的结果。
你缺少的是 Encoder
实例产生的字节流除了 per-Encoder
状态。该全局状态由[注意:此处编辑的短语]注册和发送类型组成。
当你发送一个类型化的值时,如果类型在发送前还没有被注册,它会在全局状态下为你注册。这会为该类型分配一个内部数值。请参阅 Register
(及其同伴 RegisterName
)。当您调用 X
时,它会在 X
中注册包含 s
的匿名结构类型。当您调用 Y
时,它会在 Y
中注册包含 s
的匿名结构类型。这些获得不同的内部类型编号。通过 not 调用 X
,该类型永远不会注册,并且 Y
的类型在第一个可用编号下注册。
In my code I'm hashing the bytes buffer ...
出于现在可能显而易见的原因,这不是一个好主意。 但是,如果您以已知顺序明确注册每种类型,那么您在这里就足够安全了,除非将来的某个版本出于某些(可能是好的)原因更改了有线格式。糟糕,正在测试这个表明它也无济于事。那是因为即使该类型已注册,它也不会设置 传输数 直到第一次对该类型的值进行编码。因此,您需要对每种类型的值进行编码(然后可以选择丢弃)。
Here is a functioning example 仔细丢弃编码这两种类型,以便注释掉对 log.Println(a.X())
的调用对第二个值的编码没有影响。
...这让我发疯试图理解我做错了什么!
游乐场: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"))
}
运行 产量:
{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.Buffer
和 gob.NewEncoder
,因此不清楚为什么调用 X
会更改 Y
的结果。
你缺少的是 Encoder
实例产生的字节流除了 per-Encoder
状态。该全局状态由[注意:此处编辑的短语]注册和发送类型组成。
当你发送一个类型化的值时,如果类型在发送前还没有被注册,它会在全局状态下为你注册。这会为该类型分配一个内部数值。请参阅 Register
(及其同伴 RegisterName
)。当您调用 X
时,它会在 X
中注册包含 s
的匿名结构类型。当您调用 Y
时,它会在 Y
中注册包含 s
的匿名结构类型。这些获得不同的内部类型编号。通过 not 调用 X
,该类型永远不会注册,并且 Y
的类型在第一个可用编号下注册。
In my code I'm hashing the bytes buffer ...
出于现在可能显而易见的原因,这不是一个好主意。 但是,如果您以已知顺序明确注册每种类型,那么您在这里就足够安全了,除非将来的某个版本出于某些(可能是好的)原因更改了有线格式。糟糕,正在测试这个表明它也无济于事。那是因为即使该类型已注册,它也不会设置 传输数 直到第一次对该类型的值进行编码。因此,您需要对每种类型的值进行编码(然后可以选择丢弃)。
Here is a functioning example 仔细丢弃编码这两种类型,以便注释掉对 log.Println(a.X())
的调用对第二个值的编码没有影响。