[インデックス 17885] ファイルの概要
このコミットは、Go言語の標準ライブラリであるencoding/gob
パッケージにおける、net.IP
型のエンコーディングに関する互換性の問題を修正するものです。具体的には、gob
がencoding.TextMarshaler
およびencoding.TextUnmarshaler
インターフェースを処理する際の優先順位を変更し、net.IP
型がこれらのインターフェースを実装していることによる予期せぬ挙動を回避しています。
変更されたファイルは以下の通りです。
doc/go1.2.html
: Go 1.2のリリースノートに関するドキュメント。encoding/gob
がサポートするインターフェースに関する記述が更新されています。src/pkg/encoding/gob/doc.go
:encoding/gob
パッケージのドキュメント。gob
がエンコード/デコード時に考慮するインターフェースの優先順位に関する説明が修正されています。src/pkg/encoding/gob/gobencdec_test.go
:encoding/gob
パッケージのテストファイル。net.IP
型のエンコーディング/デコーディングが正しく行われることを確認するための新しいテストケースが追加されています。src/pkg/encoding/gob/type.go
:encoding/gob
パッケージの型情報処理に関するコアロジック。MarshalText
およびUnmarshalText
インターフェースの検出と利用に関するコードが変更されています。
コミット
commit 7dd086e52d237eaf46e88c723ba61d6a835ef1d0
Author: Russ Cox <rsc@golang.org>
Date: Wed Nov 13 21:29:19 2013 -0500
encoding/gob: do not use MarshalText, UnmarshalText
This seems to be the best of a long list of bad ways to fix this issue.
Fixes #6760.
R=r
CC=golang-dev
https://golang.org/cl/22770044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7dd086e52d237eaf46e88c723ba61d6a835ef1d0
元コミット内容
encoding/gob: do not use MarshalText, UnmarshalText
This seems to be the best of a long list of bad ways to fix this issue.
Fixes #6760.
R=r
CC=golang-dev
https://golang.org/cl/22770044
変更の背景
このコミットは、Goのencoding/gob
パッケージがnet.IP
型を正しくエンコード/デコードできないという問題(Go issue #6760)を解決するために行われました。
encoding/gob
はGoのネイティブなデータシリアライゼーションフォーマットであり、Goプログラム間で構造化されたデータを効率的にやり取りするために設計されています。gob
は、カスタムのエンコーディング/デコーディングロジックを提供するために、特定のインターフェース(GobEncoder
, GobDecoder
, encoding.BinaryMarshaler
, encoding.BinaryUnmarshaler
, encoding.TextMarshaler
, encoding.TextUnmarshaler
)を型が実装しているかどうかをチェックします。
問題は、net.IP
型がencoding.TextMarshaler
およびencoding.TextUnmarshaler
インターフェースを実装していたことにありました。gob
パッケージは、これらのインターフェースをBinaryMarshaler
/BinaryUnmarshaler
よりも優先して使用しようとしていました。しかし、net.IP
のMarshalText
実装は、IPアドレスをテキスト形式(例: "192.168.1.1")で表現するものであり、これはgob
が期待するバイナリ形式とは異なりました。このミスマッチにより、net.IP
型を含むデータをgob
でエンコード/デコードしようとすると、データが破損したり、予期せぬエラーが発生したりする可能性がありました。
コミットメッセージにある「This seems to be the best of a long list of bad ways to fix this issue.」という記述は、この問題に対する解決策が複数検討されたものの、gob
の既存の設計とnet.IP
の特性を考慮すると、TextMarshaler
/TextUnmarshaler
の優先順位を下げる(あるいは無視する)ことが最も現実的で影響の少ない修正であったことを示唆しています。
前提知識の解説
encoding/gob
パッケージ: Go言語の標準ライブラリで、Goのデータ構造をバイナリ形式にシリアライズ(エンコード)およびデシリアライズ(デコード)するためのパッケージです。ネットワーク経由でのデータ転送や、ファイルへの永続化などに利用されます。gob
は、データ型情報も一緒にエンコードするため、受信側で型定義がなくてもデータを正しくデコードできるという特徴があります。encoding.BinaryMarshaler
/encoding.BinaryUnmarshaler
インターフェース:type BinaryMarshaler interface { MarshalBinary() (data []byte, err error) }
type BinaryUnmarshaler interface { UnmarshalBinary(data []byte) error }
これらのインターフェースを実装する型は、自身をバイナリ形式に変換する方法(MarshalBinary
)と、バイナリデータから自身を再構築する方法(UnmarshalBinary
)を定義できます。gob
を含む多くのGoのエンコーディングパッケージで、カスタムのバイナリシリアライゼーションロジックを提供するために利用されます。
encoding.TextMarshaler
/encoding.TextUnmarshaler
インターフェース:type TextMarshaler interface { MarshalText() (text []byte, err error) }
type TextUnmarshaler interface { UnmarshalText(text []byte) error }
これらのインターフェースを実装する型は、自身をテキスト形式に変換する方法(MarshalText
)と、テキストデータから自身を再構築する方法(UnmarshalText
)を定義できます。JSONやYAMLなど、テキストベースのエンコーディングでよく利用されます。例えば、time.Time
型はこれらのインターフェースを実装しており、日付時刻をRFC3339形式の文字列としてエンコード/デコードします。
net.IP
型: Goの標準ライブラリnet
パッケージで定義されている、IPアドレスを表す型です。[]byte
のスライスとして実装されており、IPv4アドレス(4バイト)やIPv6アドレス(16バイト)を格納します。net.IP
型は、IPアドレスの文字列表現(例: "192.168.1.1")との変換のためにMarshalText
およびUnmarshalText
を実装しています。
gob
パッケージは、エンコード/デコードを行う際に、与えられた型がこれらのインターフェースのいずれかを実装しているかをチェックし、実装していればそのカスタムロジックを優先的に利用します。従来のgob
の実装では、TextMarshaler
/TextUnmarshaler
がBinaryMarshaler
/BinaryUnmarshaler
よりも優先される可能性があり、これがnet.IP
との間で問題を引き起こしていました。
技術的詳細
このコミットの技術的詳細は、encoding/gob
パッケージがカスタムエンコーディングインターフェースを検出するロジックの変更にあります。
encoding/gob
は、Goの型をシリアライズする際に、以下の優先順位でカスタムエンコーディングロジックを探します(変更前):
GobEncoder
/GobDecoder
(gob固有のインターフェース)encoding.BinaryMarshaler
/encoding.BinaryUnmarshaler
encoding.TextMarshaler
/encoding.TextUnmarshaler
net.IP
型は、IPアドレスをテキスト形式(例: "192.168.1.1")で表現するためにMarshalText
とUnmarshalText
を実装しています。しかし、gob
は本質的にバイナリエンコーディングを目的としており、net.IP
のMarshalText
が生成するテキスト形式のデータは、gob
のバイナリストリームに直接適合しませんでした。
このコミットでは、src/pkg/encoding/gob/type.go
内のvalidUserType
関数において、TextMarshaler
およびTextUnmarshaler
インターフェースの検出ロジックをコメントアウトすることで、gob
がこれらのインターフェースをエンコーディング/デコーディングの候補から除外するように変更しています。これにより、net.IP
のような型がTextMarshaler
を実装していても、gob
はそれを無視し、代わりにその型の基盤となるバイナリ表現(この場合は[]byte
)を直接エンコード/デコードするようになります。
コミットメッセージのコメントアウトされたコードブロックには、NOTE(rsc): Would like to allow MarshalText here, but results in incompatibility with older encodings for net.IP. See golang.org/issue/6760.
と記載されており、これはMarshalText
をサポートしたいという意図はあったものの、既存のnet.IP
の古いエンコーディングとの互換性を維持するためには、MarshalText
の使用を避けるしかなかったという背景を明確に示しています。
結果として、gob
はnet.IP
をテキストとしてではなく、その基盤となるバイトスライスとして扱い、Go 1.1以前のgob
でエンコードされたnet.IP
データとの互換性を保ちつつ、正しくシリアライズ/デシリアライズできるようになりました。
コアとなるコードの変更箇所
src/pkg/encoding/gob/doc.go
--- a/src/pkg/encoding/gob/doc.go
+++ b/src/pkg/encoding/gob/doc.go
@@ -86,13 +86,13 @@ Functions and channels will not be sent in a gob. Attempting to encode such a va
at top the level will fail. A struct field of chan or func type is treated exactly
like an unexported field and is ignored.
-Gob can encode a value of any type implementing the GobEncoder,
-encoding.BinaryMarshaler, or encoding.TextMarshaler interfaces by calling the
-corresponding method, in that order of preference.
+Gob can encode a value of any type implementing the GobEncoder or
+encoding.BinaryMarshaler interfaces by calling the corresponding method,
+in that order of preference.
-Gob can decode a value of any type implementing the GobDecoder,
-encoding.BinaryUnmarshaler, or encoding.TextUnmarshaler interfaces by calling
-the corresponding method, again in that order of preference.
+Gob can decode a value of any type implementing the GobDecoder or
+encoding.BinaryUnmarshaler interfaces by calling the corresponding method,
+again in that order of preference.
Encoding Details
src/pkg/encoding/gob/gobencdec_test.go
--- a/src/pkg/encoding/gob/gobencdec_test.go
+++ b/src/pkg/encoding/gob/gobencdec_test.go
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
+ "net"
"strings"
"testing"
"time"
@@ -767,3 +768,17 @@ func TestGobEncodePtrError(t *testing.T) {
}
}
+func TestNetIP(t *testing.T) {
+ // Encoding of net.IP{1,2,3,4} in Go 1.1.
+ enc := []byte{0x07, 0x0a, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}
+
+ var ip net.IP
+ err := NewDecoder(bytes.NewReader(enc)).Decode(&ip)
+ if err != nil {
+ t.Fatalf("decode: %v", err)
+ }
+ if ip.String() != "1.2.3.4" {
+ t.Errorf("decoded to %v, want 1.2.3.4", ip.String())
+ }
+}
src/pkg/encoding/gob/type.go
--- a/src/pkg/encoding/gob/type.go
+++ b/src/pkg/encoding/gob/type.go
@@ -88,18 +88,25 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) {
ut.externalEnc, ut.encIndir = xGob, indir
} else if ok, indir := implementsInterface(ut.user, binaryMarshalerInterfaceType); ok {
ut.externalEnc, ut.encIndir = xBinary, indir
- } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok {
- ut.externalEnc, ut.encIndir = xText, indir
}
+ // NOTE(rsc): Would like to allow MarshalText here, but results in incompatibility
+ // with older encodings for net.IP. See golang.org/issue/6760.
+ // } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok {
+ // ut.externalEnc, ut.encIndir = xText, indir
+ // }
+
if ok, indir := implementsInterface(ut.user, gobDecoderInterfaceType); ok {
ut.externalDec, ut.decIndir = xGob, indir
} else if ok, indir := implementsInterface(ut.user, binaryUnmarshalerInterfaceType); ok {
ut.externalDec, ut.decIndir = xBinary, indir
- } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok {
- ut.externalDec, ut.decIndir = xText, indir
}
+ // See note above.
+ // } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok {
+ // ut.externalDec, ut.decIndir = xText, indir
+ // }
+
userTypeCache[rt] = ut
return
}
doc/go1.2.html
--- a/doc/go1.2.html
+++ b/doc/go1.2.html
@@ -736,7 +736,8 @@ now treats channel and function fields of structures as if they were unexported,
even if they are not. That is, it ignores them completely. Previously they would
trigger an error, which could cause unexpected compatibility problems if an
embedded structure added such a field.
-The package also now supports the generic encoding interfaces of the
+The package also now supports the generic <code>BinaryMarshaler</code> and
+<code>BinaryUnmarshaler</code> interfaces of the
<a href="/pkg/encoding/"><code>encoding</code></a> package
described above.
</li>
コアとなるコードの解説
src/pkg/encoding/gob/doc.go
の変更
この変更は、encoding/gob
パッケージの公式ドキュメントを更新するものです。以前の記述では、gob
がencoding.TextMarshaler
およびencoding.TextUnmarshaler
インターフェースも優先的に利用すると説明されていましたが、今回の修正によりこれらのインターフェースの優先度が実質的に削除されたため、ドキュメントもそれに合わせて修正されました。これにより、ユーザーはgob
がどのようにエンコーディングインターフェースを扱うかについて、正確な情報を得られるようになります。
src/pkg/encoding/gob/gobencdec_test.go
の変更
TestNetIP
という新しいテスト関数が追加されました。このテストは、Go 1.1でエンコードされたnet.IP{1,2,3,4}
のバイナリ表現(enc
バイトスライス)をデコードし、その結果が期待通り1.2.3.4
というIPアドレスになることを検証します。
enc := []byte{0x07, 0x0a, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}
: これは、Go 1.1のgob
でnet.IP{1,2,3,4}
がエンコードされた際の具体的なバイト列です。このバイト列をデコードすることで、新しいgob
の実装が古い形式のデータと互換性があることを確認します。NewDecoder(bytes.NewReader(enc)).Decode(&ip)
: エンコードされたバイト列からgob.Decoder
を作成し、net.IP
型の変数ip
にデコードします。if ip.String() != "1.2.3.4"
: デコードされたnet.IP
が期待される文字列表現("1.2.3.4")と一致するかを検証します。
このテストの追加は、TextMarshaler
/TextUnmarshaler
の扱いを変更したことによって、既存のnet.IP
のエンコーディング/デコーディングが壊れていないことを保証するために非常に重要です。
src/pkg/encoding/gob/type.go
の変更
このファイルは、gob
がGoの型を分析し、エンコード/デコードのための内部表現を構築する際の中心的なロジックを含んでいます。validUserType
関数は、与えられた型がGobEncoder
、BinaryMarshaler
、TextMarshaler
などのカスタムエンコーディングインターフェースを実装しているかどうかをチェックし、その情報をuserTypeInfo
構造体に格納します。
変更点としては、以下の行がコメントアウトされました。
// } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok {
// ut.externalEnc, ut.encIndir = xText, indir
// }
// } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok {
// ut.externalDec, ut.decIndir = xText, indir
// }
これらの変更により、gob
はTextMarshaler
およびTextUnmarshaler
インターフェースを実装している型であっても、それらをカスタムエンコーディングの候補として認識しなくなりました。結果として、net.IP
型がMarshalText
/UnmarshalText
を実装していても、gob
はそれらを無視し、net.IP
の基盤となる[]byte
としてのバイナリ表現を直接エンコード/デコードするようになります。
コメントアウトされたコードの上のNOTE(rsc)
コメントは、この変更の理由を明確にしています。MarshalText
を許可したいという意図はあったものの、net.IP
の古いエンコーディングとの互換性の問題(issue #6760)を解決するためには、この方法が最善であると判断されたことを示しています。
doc/go1.2.html
の変更
Go 1.2のリリースノートのドキュメントも更新され、encoding/gob
パッケージがBinaryMarshaler
とBinaryUnmarshaler
インターフェースをサポートすることが明記されました。これは、TextMarshaler
のサポートが実質的に削除されたことを反映し、ユーザーにGo 1.2でのgob
の挙動に関する正確な情報を提供します。
関連リンク
- Go issue #6760: https://golang.org/issue/6760
- Go Change List (CL) 22770044: https://golang.org/cl/22770044
参考にした情報源リンク
- コミットデータ:
./commit_data/17885.txt
- Web検索結果 (Go issue 6760): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE9-dYQ86nz7suTyslls_Jx4hl2Gralf7drmEnh98_DV7pNBqVlYxwclET-Oqhsjvhc1XDad0Z41Vhnkq8MubG3aL_nGYcsbFLZN3aMa-gF6cKqIf8P5CVxnMbij4uxGSR70gFMAWvEkLA81yTI5cuhOTP8Q_dKm4rVurKr4WHD5tOyoA_o8Khz5LZW (Google SearchによるGo issue 6760の要約)
- Go言語の公式ドキュメント (
encoding/gob
,encoding
パッケージ): https://pkg.go.dev/encoding/gob, https://pkg.go.dev/encoding (一般的なGoのエンコーディングインターフェースに関する情報) - Go言語の公式ドキュメント (
net
パッケージ): https://pkg.go.dev/net (net.IP
に関する情報)