[インデックス 11749] ファイルの概要
このコミットは、Go言語の標準ライブラリである encoding/binary
パッケージにおける Read
および Write
関数のデータ型に関する説明を改善することを目的としています。特に、「固定サイズ値 (fixed-size value)」の定義を明確にし、関連する関数のドキュメントをこの新しい定義に合わせて更新しています。また、Size
関数の実装に小さな修正が加えられ、ポインタが渡された場合でも正しく動作するように改善されています。
コミット
commit 59dc21584ace6523007642294c8c38cd7d0819f9
Author: Rob Pike <r@golang.org>
Date: Fri Feb 10 09:55:48 2012 +1100
encoding/binary: another attempt to describe the type of Read and Write's data
R=golang-dev, rsc, gri, r
CC=golang-dev
https://golang.org/cl/5641062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/59dc21584ace6523007642294c8c38cd7d0819f9
元コミット内容
encoding/binary: another attempt to describe the type of Read and Write's data
R=golang-dev, rsc, gri, r
CC=golang-dev
https://golang.org/cl/5641062
変更の背景
encoding/binary
パッケージは、Goのデータ構造とバイトシーケンス間の変換を扱うための重要なパッケージです。Read
および Write
関数は、このパッケージの中心的な機能であり、構造化されたバイナリデータを読み書きするために使用されます。これらの関数は interface{}
型の data
引数を受け取りますが、この data
引数にどのような型の値が渡されるべきか、その「デコード可能 (decodable)」または「エンコード可能 (encodable)」な値の定義が、以前のドキュメントでは曖昧であった可能性があります。
このコミットの背景には、Read
および Write
関数が処理できるデータ型の正確な定義を明確にし、ユーザーがパッケージをより正確に理解し、誤用を避けるためのドキュメント改善の必要性があったと考えられます。特に、Goの型システムにおける「固定サイズ値」という概念を導入し、それを一貫して使用することで、ドキュメントの整合性と明確性を高めることが意図されています。
また、Size
関数がポインタを受け取った場合に、そのポインタが指す実際の値のサイズを正しく計算できるようにするための修正も、このドキュメントの明確化と合わせて行われています。これは、Read
や Write
がポインタを受け取ることを許容しているため、Size
も同様にポインタを適切に扱えるべきであるという整合性の観点からも重要です。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と encoding/binary
パッケージの基本的な知識が必要です。
- Go言語の型システム: Goは静的型付け言語であり、各変数には特定の型があります。
int8
,uint8
,int16
,float32
,complex64
などは、それぞれ特定のビット幅を持つ数値型です。 interface{}
(空インターフェース): Goにおけるinterface{}
は、任意の型の値を保持できる型です。これは、Read
やWrite
のように、様々な型のデータを汎用的に扱いたい場合に利用されます。しかし、interface{}
を使用する際には、実行時に型アサーションやreflect
パッケージを用いて実際の型を検査する必要があります。encoding/binary
パッケージ: このパッケージは、Goのプリミティブ型(整数、浮動小数点数など)やそれらを含む構造体と、バイトシーケンス(バイナリデータ)との間で変換を行う機能を提供します。ネットワークプロトコル、ファイルフォーマット、シリアライゼーションなどで利用されます。ByteOrder
: バイトオーダー(エンディアン)を指定するためのインターフェースです。binary.BigEndian
とbinary.LittleEndian
が標準で提供されます。Read(r io.Reader, order ByteOrder, data interface{}) error
:io.Reader
からバイナリデータを読み込み、指定されたバイトオーダーでデコードし、data
に書き込みます。Write(w io.Writer, order ByteOrder, data interface{}) error
:data
のバイナリ表現を指定されたバイトオーダーでエンコードし、io.Writer
に書き込みます。Size(v interface{}) int
:v
をエンコードした場合に生成されるバイト数を返します。
reflect
パッケージ: Goのreflect
パッケージは、プログラムの実行時に型情報を検査したり、値を操作したりするための機能を提供します。reflect.ValueOf(v interface{}) Value
: 任意のGoの値をreflect.Value
型に変換します。これにより、実行時にその値の型や内容を調べることができます。reflect.Indirect(v reflect.Value) reflect.Value
:v
がポインタの場合、そのポインタが指す先の値(間接参照された値)を返します。v
がポインタでない場合は、v
そのものを返します。これは、ポインタの有無にかかわらず、常に基になる値に対して操作を行いたい場合に便利です。
- 固定サイズ値 (Fixed-size values):
encoding/binary
の文脈における「固定サイズ値」とは、そのメモリ上のサイズがコンパイル時に決定され、実行中に変化しない値のことです。これには、int8
,uint8
,float32
などのプリミティブな数値型、およびそれらの型の要素のみを含む配列や構造体が含まれます。スライスや文字列のように、実行時にサイズが変動する可能性のある型は、通常、固定サイズ値とは見なされません。encoding/binary
パッケージは、主にこのような固定サイズ値のバイナリ変換を効率的に行うために設計されています。
技術的詳細
このコミットの技術的な変更点は、主に encoding/binary
パッケージのドキュメントの明確化と、Size
関数の挙動の改善にあります。
ドキュメントの変更
最も重要な変更は、パッケージレベルで「固定サイズ値 (fixed-size value)」の明確な定義が追加されたことです。
// Package binary implements translation between
// unsigned integer values and byte sequences
// and the reading and writing of fixed-size values.
// A fixed-size value is either a fixed-size arithmetic
// type (int8, uint8, int16, float32, complex64, ...)
// or an array or struct containing only fixed-size values.
package binary
この定義により、encoding/binary
パッケージが扱うデータの種類が明確になりました。具体的には、以下のいずれかの条件を満たす型が「固定サイズ値」と定義されています。
- 固定サイズの算術型:
int8
,uint8
,int16
,float32
,complex64
など、メモリ上のサイズが固定されているプリミティブな数値型。 - 固定サイズ値のみを含む配列または構造体: 配列の場合、その要素がすべて固定サイズ値であること。構造体の場合、そのフィールドがすべて固定サイズ値であること。
この新しい定義に基づいて、Read
, Write
, Size
関数のドキュメントが更新されました。以前は「デコード可能 (decodable)」や「エンコード可能 (encodable)」という用語が使われていましたが、これらが「固定サイズ値」というより具体的な用語に置き換えられました。
Read
関数のドキュメント変更:
- 変更前:
Data must be a pointer to a decodable value or a slice of decodable values.
- 変更後:
Data must be a pointer to a fixed-size value or a slice of fixed-size values.
Write
関数のドキュメント変更:
- 変更前:
Data must be an encodable value or a pointer to an encodable value.
- 変更後:
Data must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
Size
関数のドキュメント変更:
- 変更前:
Size returns how many bytes Write would generate to encode the value v, assuming the Write would succeed.
- 変更後:
Size returns how many bytes Write would generate to encode the value v, which must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
これらの変更により、各関数が期待する data
引数の型が、パッケージ全体で一貫した「固定サイズ値」という概念で説明されるようになりました。
Size
関数の実装変更
Size
関数の実装にも重要な変更が加えられました。
- 変更前:
return dataSize(reflect.ValueOf(v))
- 変更後:
return dataSize(reflect.Indirect(reflect.ValueOf(v)))
この変更は、Size
関数にポインタが渡された場合に、そのポインタが指す実際の値のサイズを計算できるようにするためのものです。reflect.ValueOf(v)
は v
の reflect.Value
を返しますが、v
がポインタの場合、それはポインタ自体の reflect.Value
になります。reflect.Indirect()
を適用することで、v
がポインタであればそのポインタが指す先の値の reflect.Value
を取得し、ポインタでなければ v
そのものの reflect.Value
を取得します。これにより、dataSize
関数は常に基になる値に対して動作し、ポインタの有無にかかわらず正しいサイズを返すことが保証されます。
この修正は、Write
関数が「ポインタ」も受け入れるというドキュメントの変更と整合しています。Write
がポインタを受け入れてその指す先の値をエンコードするならば、Size
も同様にポインタが指す先の値のサイズを計算できるべきです。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、src/pkg/encoding/binary/binary.go
ファイル内の Size
関数の実装です。
--- a/src/pkg/encoding/binary/binary.go
+++ b/src/pkg/encoding/binary/binary.go
@@ -253,10 +250,10 @@ func Write(w *Writer, order ByteOrder, data interface{}) error {
return err
}
-// Size returns how many bytes Write would generate to encode the value v, assuming
-// the Write would succeed.
+// Size returns how many bytes Write would generate to encode the value v, which
+// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
func Size(v interface{}) int {
- return dataSize(reflect.ValueOf(v))
+ return dataSize(reflect.Indirect(reflect.ValueOf(v)))
}
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
コアとなるコードの解説
変更された Size
関数の行は以下の通りです。
return dataSize(reflect.Indirect(reflect.ValueOf(v)))
reflect.ValueOf(v)
: これは、Size
関数に渡されたinterface{}
型の引数v
をreflect.Value
型に変換します。reflect.Value
は、Goの実行時リフレクションシステムにおける値の抽象表現です。reflect.Indirect(...)
: この関数は、reflect.Value
がポインタを表している場合、そのポインタが指す先の値のreflect.Value
を返します。もしreflect.Value
がポインタでなければ、reflect.Indirect
は元のreflect.Value
をそのまま返します。- 例:
v
が*int
型のポインタであれば、reflect.Indirect
はint
型のreflect.Value
を返します。 - 例:
v
がint
型の値であれば、reflect.Indirect
はint
型のreflect.Value
をそのまま返します。
- 例:
dataSize(...)
: この関数(コミットには含まれていませんが、encoding/binary
パッケージの内部関数)は、reflect.Value
が表す値のバイナリサイズを計算します。
この変更により、Size
関数は、v
が直接の固定サイズ値であっても、その固定サイズ値へのポインタであっても、常に正しくそのバイナリサイズを計算できるようになりました。これは、encoding/binary
パッケージの Read
および Write
関数がポインタを受け入れるという仕様と一貫性を持たせるための重要な修正です。
関連リンク
- Go言語の
encoding/binary
パッケージのドキュメント: https://pkg.go.dev/encoding/binary - Go言語の
reflect
パッケージのドキュメント: https://pkg.go.dev/reflect - Gerrit Code Review (Goプロジェクトのコードレビューシステム): https://golang.org/cl/5641062
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語の
encoding/binary
およびreflect
パッケージのソースコード - Go言語のコミット履歴とGerritの変更リスト
- Go言語におけるリフレクションに関する一般的な解説記事