Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17220] ファイルの概要

このコミットは、Go言語の encoding/gob パッケージが、Go 1.2で導入された encoding パッケージ内の新しい汎用インターフェース(encoding.BinaryMarshaler, encoding.BinaryUnmarshaler, encoding.TextMarshaler, encoding.TextUnmarshaler)をサポートするように拡張するものです。これにより、gob エンコーディング/デコーディングにおいて、より多様な型がカスタムシリアライズ/デシリアライズロジックを提供できるようになります。

コミット

commit ce3ba126a0c5370c2c4c4e1ef32291316b96b5b0
Author: Russ Cox <rsc@golang.org>
Date:   Wed Aug 14 00:18:48 2013 -0400

    encoding/gob: support new generic interfaces in package encoding
    
    R=r
    CC=golang-dev
    https://golang.org/cl/12681044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ce3ba126a0c5370c2c4c4e1ef32291316b96b5b0

元コミット内容

encoding/gob: support new generic interfaces in package encoding

R=r
CC=golang-dev
https://golang.org/cl/12681044

変更の背景

Go言語の encoding パッケージは、様々なデータ形式(JSON, XML, Gobなど)間でデータを変換するための標準的なインターフェースを提供します。このコミットが行われた2013年当時、Go 1.2のリリースに向けて、encoding.BinaryMarshaler/BinaryUnmarshaler および encoding.TextMarshaler/TextUnmarshaler といった、より汎用的なデータ変換インターフェースが導入されつつありました。

encoding/gob はGo独自のバイナリシリアライゼーション形式であり、Goプログラム間で構造化されたデータを効率的に送受信するために設計されています。これまで gob は、カスタムエンコーディング/デコーディングのために GobEncoder および GobDecoder という独自のインターフェースをサポートしていました。

この変更の背景には、Goのエコシステム全体でデータ変換の統一性を高めるという目的があります。新しい汎用インターフェースを gob がサポートすることで、開発者は gob だけでなく、他の encoding パッケージ(例: encoding/json)でも同じカスタムシリアライゼーションロジックを再利用できるようになります。これにより、コードの重複を減らし、より柔軟なデータ処理が可能になります。

具体的には、gob がこれらの新しいインターフェースを認識し、既存の GobEncoder/GobDecoder と同様に、型のカスタムエンコーディング/デコーディングを処理できるようにすることが目的です。これにより、gob の利用者は、gob 固有のインターフェースだけでなく、より広範な encoding パッケージのインターフェースを利用して、自身の型をシリアライズ/デシリアライズできるようになります。

前提知識の解説

Go言語の encoding/gob パッケージ

encoding/gob は、Go言語のプログラム間でGoのデータ構造をエンコードおよびデコードするためのバイナリシリアライゼーション形式です。主な特徴は以下の通りです。

  • 自己記述的: gob ストリームは、データだけでなく、そのデータの型情報も含まれています。これにより、受信側は事前に型を知らなくてもデータをデコードできます。
  • 効率性: バイナリ形式であるため、JSONやXMLなどのテキストベースの形式よりもコンパクトで高速です。
  • Go固有: Goの型システムに密接に統合されており、構造体、スライス、マップ、プリミティブ型などを直接エンコード/デコードできます。
  • カスタムエンコーディング: GobEncoder および GobDecoder インターフェースを実装することで、型は自身のエンコード/デコード方法をカスタマイズできます。

GobEncoder および GobDecoder インターフェース

encoding/gob パッケージは、カスタムシリアライゼーションを可能にするために以下のインターフェースを定義しています。

type GobEncoder interface {
    GobEncode() ([]byte, error)
}

type GobDecoder interface {
    GobDecode([]byte) error
}

これらのインターフェースを型が実装している場合、gob はその型のデフォルトのシリアライゼーションロジックではなく、GobEncode または GobDecode メソッドを呼び出してデータの変換を行います。

Go言語の encoding パッケージと汎用インターフェース

Go 1.2で導入された encoding パッケージは、より汎用的なデータ変換インターフェースを提供します。これらは、特定のエンコーディング形式に依存しない、データ表現の標準化を目指しています。

encoding.BinaryMarshaler および encoding.BinaryUnmarshaler

バイナリ形式でのカスタムシリアライゼーション/デシリアライゼーションを定義するためのインターフェースです。

type BinaryMarshaler interface {
    MarshalBinary() ([]byte, error)
}

type BinaryUnmarshaler interface {
    UnmarshalBinary([]byte) error
}

これらのインターフェースは、型が自身をバイナリ形式に変換する方法、またはバイナリデータから自身を再構築する方法を定義するために使用されます。

encoding.TextMarshaler および encoding.TextUnmarshaler

テキスト形式でのカスタムシリアライゼーション/デシリアライゼーションを定義するためのインターフェースです。

type TextMarshaler interface {
    MarshalText() ([]byte, error)
}

type TextUnmarshaler interface {
    UnmarshalText([]byte) error
}

これらのインターフェースは、型が自身をテキスト形式に変換する方法、またはテキストデータから自身を再構築する方法を定義するために使用されます。例えば、fmt.Stringer とは異なり、これはデータ変換に特化しています。

技術的詳細

このコミットの技術的詳細は、encoding/gob パッケージがどのようにして新しい汎用 encoding インターフェースを認識し、処理するように変更されたかにあります。

  1. userTypeInfo 構造体の拡張: src/pkg/encoding/gob/type.go にある userTypeInfo 構造体は、Goの型に関するメタデータを保持します。このコミットでは、isGobEncoderisGobDecoder フィールドが削除され、代わりに externalEncexternalDec という新しいフィールドが追加されました。これらは、型がどの外部エンコーディング/デコーディングインターフェース(GobEncoder, BinaryMarshaler, TextMarshaler)を実装しているかを示す整数値(ビットフラグ)として機能します。

    type userTypeInfo struct {
        // ... (既存フィールド)
        externalEnc int // xGob, xBinary, or xText
        externalDec int // xGob, xBinary or xText
        // ... (既存フィールド)
    }
    
    // externalEncoding bits
    const (
        xGob    = 1 + iota // GobEncoder or GobDecoder
        xBinary            // encoding.BinaryMarshaler or encoding.BinaryUnmarshaler
        xText              // encoding.TextMarshaler or encoding.TextUnmarshaler
    )
    

    validUserType 関数内で、reflect.Type がこれらの新しいインターフェースを実装しているかどうかがチェックされ、対応する xGob, xBinary, xText の値が externalEnc または externalDec に設定されます。

  2. エンコーディングロジックの変更 (encode.go): encodeGobEncoder 関数は、GobEncoder インターフェースを実装する型のエンコーディングを処理していましたが、このコミットにより、ut.externalEnc の値に基づいて GobEncoder, BinaryMarshaler, TextMarshaler のいずれかのメソッドを呼び出すように変更されました。

    func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, ut *userTypeInfo, v reflect.Value) {
        var data []byte
        var err error
        switch ut.externalEnc {
        case xGob:
            data, err = v.Interface().(GobEncoder).GobEncode()
        case xBinary:
            data, err = v.Interface().(encoding.BinaryMarshaler).MarshalBinary()
        case xText:
            data, err = v.Interface().(encoding.TextMarshaler).MarshalText()
        }
        // ... (エラーハンドリングとデータ書き込み)
    }
    

    また、encOpForcompileEnc などの関数も、型のエンコーディング方法を決定する際に ut.isGobEncoder の代わりに ut.externalEnc != 0 をチェックするように更新されました。

  3. デコーディングロジックの変更 (decode.go): decodeGobDecoder 関数も同様に、GobDecoder インターフェースを実装する型のデコーディングを処理していましたが、ut.externalDec の値に基づいて GobDecoder, BinaryUnmarshaler, TextUnmarshaler のいずれかのメソッドを呼び出すように変更されました。

    func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value) {
        // ... (データ読み込み)
        switch ut.externalDec {
        case xGob:
            err = v.Interface().(GobDecoder).GobDecode(b)
        case xBinary:
            err = v.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b)
        case xText:
            err = v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b)
        }
        // ... (エラーハンドリング)
    }
    

    decOpForcompatibleType などの関数も、型のデコーディング方法を決定する際に ut.isGobDecoder の代わりに ut.externalDec != 0 をチェックするように更新されました。

  4. 型情報の伝達 (type.go, debug.go): gob は、エンコード/デコード時に型情報を交換します。このコミットでは、wireType 構造体に BinaryMarshalerTTextMarshalerT フィールドが追加され、これらの新しいインターフェースを実装する型が gob ストリーム内で適切に識別されるようになりました。

    type wireType struct {
        // ... (既存フィールド)
        GobEncoderT      *gobEncoderType
        BinaryMarshalerT *gobEncoderType // New
        TextMarshalerT   *gobEncoderType // New
    }
    

    debug.gotypeDefinition 関数も、これらの新しい wireType フィールドを処理するように更新され、デバッグ情報が正しく表示されるようになりました。

  5. ドキュメントの更新 (doc.go): src/pkg/encoding/gob/doc.go のドキュメントが更新され、gobencoding.BinaryMarshaler, encoding.TextMarshaler およびそれらの Unmarshaler インターフェースをサポートすることが明記されました。これにより、ユーザーはこれらの新しい機能について知ることができます。

  6. テストの追加/更新 (gobencdec_test.go): src/pkg/encoding/gob/gobencdec_test.go には、BinaryGobber, TextGobber などの新しいテスト型が追加され、これらの型が BinaryMarshaler/Unmarshaler および TextMarshaler/Unmarshaler インターフェースを実装していることを確認するテストケースが追加されました。これにより、新しい機能が正しく動作することが保証されます。

これらの変更により、encoding/gob はGoの標準 encoding インターフェースとの互換性を高め、より柔軟なデータシリアライゼーション/デシリアライゼーションの選択肢を開発者に提供するようになりました。

コアとなるコードの変更箇所

src/pkg/encoding/gob/decode.go

--- a/src/pkg/encoding/gob/decode.go
+++ b/src/pkg/encoding/gob/decode.go
@@ -767,15 +768,22 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
 
 // decodeGobDecoder decodes something implementing the GobDecoder interface.
 // The data is encoded as a byte slice.
-func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) {
+func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value) {
 	// Read the bytes for the value.
 	b := make([]byte, state.decodeUint())
 	_, err := state.b.Read(b)
 	if err != nil {
 		error_(err)
 	}
-	// We know it's a GobDecoder, so just call the method directly.
-	err = v.Interface().(GobDecoder).GobDecode(b)
+	// We know it's one of these.
+	switch ut.externalDec {
+	case xGob:
+		err = v.Interface().(GobDecoder).GobDecode(b)
+	case xBinary:
+		err = v.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b)
+	case xText:
+		err = v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b)
+	}
 	if err != nil {
 		error_(err)
 	}
@@ -825,9 +833,10 @@ var decIgnoreOpMap = map[typeId]decOp{
 func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) {
 	ut := userType(rt)
 	// If the type implements GobEncoder, we handle it without further processing.
-	if ut.isGobDecoder {
+	if ut.externalDec != 0 {
 		return dec.gobDecodeOpFor(ut)
 	}
+
 	// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
 	// Return the pointer to the op we're already building.
 	if opPtr := inProgress[rt]; opPtr != nil {
@@ -954,7 +963,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp {
 			}
 
-\t\tcase wire.GobEncoderT != nil:\
+\t\tcase wire.GobEncoderT != nil, wire.BinaryMarshalerT != nil, wire.TextMarshalerT != nil:\
 			op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
 				state.dec.ignoreGobDecoder(state)
 			}
@@ -993,7 +1002,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) {
 		} else {
 			v = reflect.NewAt(rcvrType, p).Elem()
 		}
-\t\tstate.dec.decodeGobDecoder(state, v)\
+\t\tstate.dec.decodeGobDecoder(ut, state, v)\
 	}\
 	return &op, int(ut.indir)\
 
@@ -1010,12 +1019,18 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re\
 	inProgress[fr] = fw
 	ut := userType(fr)
 	wire, ok := dec.wireType[fw]
-	// If fr is a GobDecoder, the wire type must be GobEncoder.
-	// And if fr is not a GobDecoder, the wire type must not be either.
-	if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct.
+	// If wire was encoded with an encoding method, fr must have that method.
+	// And if not, it must not.
+	// At most one of the booleans in ut is set.
+	// We could possibly relax this constraint in the future in order to
+	// choose the decoding method using the data in the wireType.
+	// The parentheses look odd but are correct.
+	if (ut.externalDec == xGob) != (ok && wire.GobEncoderT != nil) ||
+		(ut.externalDec == xBinary) != (ok && wire.BinaryMarshalerT != nil) ||
+		(ut.externalDec == xText) != (ok && wire.TextMarshalerT != nil) {
 		return false
 	}
-\tif ut.isGobDecoder { // This test trumps all others.\
+\tif ut.externalDec != 0 { // This test trumps all others.\
 		return true
 	}\
 	switch t := ut.base; t.Kind() {
@@ -1114,8 +1129,7 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err\
 func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) {
 	rt := ut.base
 	srt := rt
-\tif srt.Kind() != reflect.Struct ||\
-\t\tut.isGobDecoder {\
+\tif srt.Kind() != reflect.Struct || ut.externalDec != 0 {\
 		return dec.compileSingle(remoteId, ut)
 	}
 	var wireStruct *structType
@@ -1223,7 +1237,7 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) {
 		return
 	}
 	engine := *enginePtr
-\tif st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder {\
+\tif st := base; st.Kind() == reflect.Struct && ut.externalDec == 0 {\
 		if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 {
 			name := base.Name()
 			errorf("type mismatch: no fields matched compiling decoder for %s", name)

src/pkg/encoding/gob/type.go

--- a/src/pkg/encoding/gob/type.go
+++ b/src/pkg/encoding/gob/type.go
@@ -18,14 +19,21 @@ import (
 // to the package.  It's computed once and stored in a map keyed by reflection
 // type.
 type userTypeInfo struct {
-\tuser         reflect.Type // the type the user handed us
-\tbase         reflect.Type // the base type after all indirections
-\tindir        int          // number of indirections to reach the base type
-\tisGobEncoder bool         // does the type implement GobEncoder?
-\tisGobDecoder bool         // does the type implement GobDecoder?
-\tencIndir     int8         // number of indirections to reach the receiver type; may be negative
-\tdecIndir     int8         // number of indirections to reach the receiver type; may be negative
-}\
+\tuser        reflect.Type // the type the user handed us
+\tbase        reflect.Type // the base type after all indirections
+\tindir       int          // number of indirections to reach the base type
+\texternalEnc int          // xGob, xBinary, or xText
+\texternalDec int          // xGob, xBinary or xText
+\tencIndir    int8         // number of indirections to reach the receiver type; may be negative
+\tdecIndir    int8         // number of indirections to reach the receiver type; may be negative
+}\
+
+// externalEncoding bits
+const (
+\txGob    = 1 + iota // GobEncoder or GobDecoder
+\txBinary            // encoding.BinaryMarshaler or encoding.BinaryUnmarshaler
+\txText              // encoding.TextMarshaler or encoding.TextUnmarshaler
+)\
 
 var (\
 \t// Protected by an RWMutex because we read it a lot and write
@@ -75,15 +83,34 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) {
 \t\t}\
 \t\tut.indir++\
 \t}\
-\tut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType)\
-\tut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType)\
+\
+\tif ok, indir := implementsInterface(ut.user, gobEncoderInterfaceType); ok {
+\t\tut.externalEnc, ut.encIndir = xGob, indir
+\t} else if ok, indir := implementsInterface(ut.user, binaryMarshalerInterfaceType); ok {
+\t\tut.externalEnc, ut.encIndir = xBinary, indir
+\t} else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok {
+\t\tut.externalEnc, ut.encIndir = xText, indir
+\t}\
+\
+\tif ok, indir := implementsInterface(ut.user, gobDecoderInterfaceType); ok {
+\t\tut.externalDec, ut.decIndir = xGob, indir
+\t} else if ok, indir := implementsInterface(ut.user, binaryUnmarshalerInterfaceType); ok {
+\t\tut.externalDec, ut.decIndir = xBinary, indir
+\t} else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok {
+\t\tut.externalDec, ut.decIndir = xText, indir
+\t}\
+\
 \tuserTypeCache[rt] = ut
 \treturn
 }\
 
 var (\
-\tgobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem()\
-\tgobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem()\
+\tgobEncoderInterfaceType        = reflect.TypeOf((*GobEncoder)(nil)).Elem()\
+\tgobDecoderInterfaceType        = reflect.TypeOf((*GobDecoder)(nil)).Elem()\
+\tbinaryMarshalerInterfaceType   = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()\
+\tbinaryUnmarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()\
+\ttextMarshalerInterfaceType     = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()\
+\ttextUnmarshalerInterfaceType   = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()\
 )\
 
 // implementsInterface reports whether the type implements the
@@ -593,11 +620,13 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId {
 // To maintain binary compatibility, if you extend this type, always put
 // the new fields last.
 type wireType struct {
-\tArrayT      *arrayType
-\tSliceT      *sliceType
-\tStructT     *structType
-\tMapT        *mapType
-\tGobEncoderT *gobEncoderType
+\tArrayT           *arrayType
+\tSliceT           *sliceType
+\tStructT          *structType
+\tMapT             *mapType
+\tGobEncoderT      *gobEncoderType
+\tBinaryMarshalerT *gobEncoderType
+\tTextMarshalerT   *gobEncoderType
 }\
 
 func (w *wireType) string() string {
@@ -631,12 +660,20 @@ var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock
 // typeLock must be held.
 func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) {
 \trt := ut.base
-\tif ut.isGobEncoder {\
+\tif ut.externalEnc != 0 {\
 \t\t// We want the user type, not the base type.\
 \t\trt = ut.user\
 \t}\
 \tif _, alreadySent := enc.sent[rt]; !alreadySent {\
 \t\t// ... (既存ロジック)
 \t}\
-\tif ut.isGobEncoder {\
+\tif ut.externalEnc != 0 {\
 \t\tuserType, err := getType(rt.Name(), ut, rt)\
 \t\tif err != nil {\
 \t\t\treturn nil, err\
 \t\t}\
-\t\tinfo.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)}\
+\t\tgt := userType.id().gobType().(*gobEncoderType)\
+\t\tswitch ut.externalEnc {\
+\t\tcase xGob:\
+\t\t\tinfo.wire = &wireType{GobEncoderT: gt}\
+\t\tcase xBinary:\
+\t\t\tinfo.wire = &wireType{BinaryMarshalerT: gt}\
+\t\tcase xText:\
+\t\t\tinfo.wire = &wireType{TextMarshalerT: gt}\
+\t\t}\
 \t\ttypeInfoMap[ut.user] = info
 \t\treturn info, nil
 \t}\

コアとなるコードの解説

src/pkg/encoding/gob/decode.go の変更点

  • decodeGobDecoder 関数のシグネチャ変更とロジック拡張:

    • 変更前: func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value)
    • 変更後: func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value)
    • userTypeInfo (ut) パラメータが追加され、デコード対象の型がどの外部インターフェースを実装しているか(xGob, xBinary, xText)を ut.externalDec を通じて判断できるようになりました。
    • switch ut.externalDec 文が導入され、GobDecoder, encoding.BinaryUnmarshaler, encoding.TextUnmarshaler のいずれかのメソッドを動的に呼び出すようになりました。これにより、gob はこれらの新しいインターフェースを実装する型を適切にデコードできます。
  • decOpFor 関数の条件変更:

    • 変更前: if ut.isGobDecoder { ... }
    • 変更後: if ut.externalDec != 0 { ... }
    • 型が GobDecoder を実装しているかどうかのチェックが、いずれかの外部デコーディングインターフェースを実装しているかどうかのチェックに汎用化されました。
  • decIgnoreOpFor 関数の条件変更:

    • 変更前: case wire.GobEncoderT != nil:
    • 変更後: case wire.GobEncoderT != nil, wire.BinaryMarshalerT != nil, wire.TextMarshalerT != nil:
    • gob ストリーム内の型情報(wireType)が、GobEncoderT だけでなく、新しく追加された BinaryMarshalerTTextMarshalerT を持っている場合も、カスタムデコーディングロジックを無視する(スキップする)ように変更されました。これは、エンコード時にカスタムエンコーダが使用された場合、デコード時も対応するカスタムデコーダで処理されるべきであり、通常の構造体デコードロジックは適用されないためです。
  • compatibleType 関数の条件変更:

    • 変更前は ut.isGobDecoderwire.GobEncoderT の関係のみをチェックしていましたが、変更後は ut.externalDecwire.GobEncoderT, wire.BinaryMarshalerT, wire.TextMarshalerT のそれぞれの組み合わせを厳密にチェックするようになりました。これにより、エンコードされた型とデコードされる型が、どのカスタムインターフェースを使用しているかについて互換性があることを保証します。
  • compileDec および decodeValue 関数の条件変更:

    • これらの関数も、構造体のコンパイルや値のデコードを行う際に、ut.isGobDecoder の代わりに ut.externalDec != 0 を使用して、型がカスタムデコーディングインターフェースを実装しているかどうかを判断するように変更されました。

src/pkg/encoding/gob/type.go の変更点

  • userTypeInfo 構造体の定義変更:

    • isGobEncoderisGobDecoder ブール値フィールドが削除され、代わりに externalEncexternalDec という整数値フィールドが追加されました。
    • xGob, xBinary, xText という定数が定義され、externalEnc/externalDec がこれらの値を取ることで、型がどのエンコーディング/デコーディングインターフェースを実装しているかを示すようになりました。
  • validUserType 関数のロジック変更:

    • implementsInterface 関数を使用して、GobEncoder, GobDecoder に加えて、encoding.BinaryMarshaler, encoding.BinaryUnmarshaler, encoding.TextMarshaler, encoding.TextUnmarshaler の各インターフェースを型が実装しているかをチェックするようになりました。
    • 実装しているインターフェースに応じて、ut.externalEnc または ut.externalDec に対応する定数(xGob, xBinary, xText)を設定します。優先順位は GobEncoder/Decoder -> BinaryMarshaler/Unmarshaler -> TextMarshaler/Unmarshaler の順です。
  • 新しいインターフェース型の定義:

    • binaryMarshalerInterfaceType, binaryUnmarshalerInterfaceType, textMarshalerInterfaceType, textUnmarshalerInterfaceType という reflect.Type 変数が追加され、これらのインターフェースの型情報を保持するようになりました。これらは implementsInterface 関数でインターフェースの実装チェックを行う際に使用されます。
  • wireType 構造体の定義変更:

    • BinaryMarshalerTTextMarshalerT という新しいフィールドが追加されました。これにより、gob ストリーム内で、型が BinaryMarshaler または TextMarshaler を実装していることを示す型情報を伝達できるようになりました。
  • getTypeInfo 関数のロジック変更:

    • 型情報マップに登録する際に、ut.isGobEncoder の代わりに ut.externalEnc != 0 を使用して、型がカスタムエンコーディングインターフェースを実装しているかどうかを判断するようになりました。
    • info.wire の設定時に、ut.externalEnc の値に基づいて GobEncoderT, BinaryMarshalerT, TextMarshalerT のいずれかを wireType に設定するようになりました。

これらの変更は、encoding/gob がGoの標準 encoding インターフェースを透過的に扱えるようにするための基盤を構築しています。

関連リンク

参考にした情報源リンク