[インデックス 14223] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/binary
パッケージ内の varint.go
ファイルに対する変更です。encoding/binary
パッケージは、Goのデータ構造とバイトシーケンス間の変換、特に可変長整数(varint)のエンコードとデコードを提供します。varint.go
は、このパッケージ内で可変長整数の読み書きに関する具体的な実装を含んでいます。
コミット
encoding/binary
: ReadVarint reads a signed number, not unsigned number
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f9902c7197f436578e8fafa7946d8cd83467729e
元コミット内容
commit f9902c7197f436578e8fafa7946d8cd83467729e
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Oct 26 21:14:34 2012 +0800
encoding/binary: ReadVarint reads a signed number, not unsigned number
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/6762051
変更の背景
このコミットの背景は、encoding/binary
パッケージの ReadVarint
関数のコメントが、その実際の動作と一致していなかったことです。ReadVarint
関数は、内部的に符号なし整数を読み取る ReadUvarint
を利用しつつ、結果として符号付き整数を正しくデコードするロジック(ジグザグエンコーディング)を含んでいました。しかし、既存のコメントは「符号なし整数を読み取る」と誤って記述されており、これが関数の意図や動作を誤解させる可能性がありました。
この変更は、コードの動作自体を変更するものではなく、ドキュメント(コメント)を修正することで、関数の真の目的と動作を正確に反映させ、開発者の混乱を防ぐことを目的としています。これは、コードの可読性と保守性を向上させるための重要なステップです。
前提知識の解説
1. 可変長整数 (Varint)
可変長整数(Varint)は、数値を効率的にエンコードするための手法です。特に、小さい数値は少ないバイト数で表現し、大きい数値はより多くのバイト数で表現することで、全体的なデータサイズを削減します。GoogleのProtocol Buffersなどで広く使用されています。
Varintの基本的な考え方は、各バイトの最上位ビット(MSB)を「継続ビット」として使用することです。MSBが1の場合、その数値は次のバイトにも続くことを示し、MSBが0の場合、そのバイトが数値の最後のバイトであることを示します。残りの7ビットが数値のデータとして使用されます。
2. 符号付き整数と符号なし整数
- 符号なし整数 (Unsigned Integer): 0以上の整数のみを表現します。すべてのビットが数値の大きさを表すために使用されます。
- 符号付き整数 (Signed Integer): 正の数、負の数、および0を表現します。通常、最上位ビットが符号ビットとして使用され、0は正、1は負を示します(2の補数表現が一般的)。
3. ジグザグエンコーディング (ZigZag Encoding)
Varintは通常、符号なし整数をエンコードするために設計されています。しかし、負の数をVarintで効率的に表現するためには、ジグザグエンコーディングが使用されます。
ジグザグエンコーディングは、符号付き整数を符号なし整数にマッピングする手法です。これにより、絶対値が小さい負の数(例: -1, -2)も、絶対値が小さい正の数(例: 1, 2)と同様に、Varintで少ないバイト数で表現できるようになります。
マッピングのルールは以下の通りです(例: 32ビット整数):
n
が正の場合:(n << 1)
n
が負の場合:(~n << 1) | 1
これにより、以下のようなマッピングが行われます。
- 0 -> 0
- -1 -> 1
- 1 -> 2
- -2 -> 3
- 2 -> 4
- ...
デコード時には、この逆の操作を行います。
ux
が偶数の場合:ux >> 1
ux
が奇数の場合:~(ux >> 1)
このコミットで修正された ReadVarint
関数は、まさにこのジグザグエンコーディングのデコードロジック(x := int64(ux >> 1)
と if ux&1 != 0 { x = ^x }
)を含んでいます。
技術的詳細
encoding/binary
パッケージの ReadVarint
関数は、io.ByteReader
からバイトを読み取り、それを int64
型の符号付き整数としてデコードします。この関数は、内部的に ReadUvarint
関数を呼び出して、まずバイト列を符号なしの uint64
として読み取ります。その後、読み取った uint64
の値に対してジグザグデコーディングを適用し、元の符号付き int64
の値に変換します。
ジグザグデコーディングのロジックは以下の通りです。
ux, err := ReadUvarint(r)
: まず、バイトリーダーr
から符号なし可変長整数ux
を読み取ります。x := int64(ux >> 1)
: 読み取ったux
を1ビット右シフトし、int64
にキャストします。これはジグザグエンコーディングのデコードの一部で、正の数と負の数の絶対値を復元するステップです。if ux&1 != 0 { x = ^x }
:ux
の最下位ビット(LSB)が1であるかどうかをチェックします。LSBが1の場合、元の数値は負の数であったことを示します。この場合、x
のビットを反転(1の補数)することで、元の負の数を復元します。
この一連の処理により、ReadVarint
は正しく符号付き整数をデコードします。したがって、以前のコメントが「符号なし整数を読み取る」と記述していたのは誤りであり、実際の動作は「符号付き整数を読み取る」ものでした。このコミットは、そのコメントの誤りを修正し、関数の振る舞いを正確に反映させるものです。
コアとなるコードの変更箇所
--- a/src/pkg/encoding/binary/varint.go
+++ b/src/pkg/encoding/binary/varint.go
@@ -123,7 +123,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) {
panic("unreachable")
}
-// ReadVarint reads an encoded unsigned integer from r and returns it as an int64.
+// ReadVarint reads an encoded signed integer from r and returns it as an int64.
func ReadVarint(r io.ByteReader) (int64, error) {
ux, err := ReadUvarint(r) // ok to continue in presence of error
x := int64(ux >> 1)
コアとなるコードの解説
変更されたのは、ReadVarint
関数の直前にあるコメント行のみです。
- // ReadVarint reads an encoded unsigned integer from r and returns it as an int64.
- これは変更前のコメントで、「符号なし整数を読み取る」と誤って記述されていました。
+ // ReadVarint reads an encoded signed integer from r and returns it as an int64.
- これは変更後のコメントで、「符号付き整数を読み取る」と修正されています。
この変更は、コードの実行ロジックには一切影響を与えません。ReadVarint
関数の実際の動作は、変更前も変更後も一貫して符号付き整数をデコードしていました。この修正は、関数のドキュメントとしてのコメントを、その実際の機能と一致させるためのものです。これにより、この関数を使用する開発者が、その目的と戻り値の型について正確な理解を得られるようになります。
関連リンク
- Go CL 6762051: https://golang.org/cl/6762051
参考にした情報源リンク
- Protocol Buffers - Encoding: https://developers.google.com/protocol-buffers/docs/encoding#varints
- GoDoc for encoding/binary: https://pkg.go.dev/encoding/binary
- ZigZag Encoding Explained: https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding
- Go source code for encoding/binary/varint.go (at the time of commit): (具体的なリンクはコミット時点のGoリポジトリの履歴を辿る必要がありますが、現在の最新版でも同様のロジックは確認できます)# [インデックス 14223] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/binary
パッケージ内の varint.go
ファイルに対する変更です。encoding/binary
パッケージは、Goのデータ構造とバイトシーケンス間の変換、特に可変長整数(varint)のエンコードとデコードを提供します。varint.go
は、このパッケージ内で可変長整数の読み書きに関する具体的な実装を含んでいます。
コミット
encoding/binary
: ReadVarint reads a signed number, not unsigned number
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f9902c7197f436578e8fafa7946d8cd83467729e
元コミット内容
commit f9902c7197f436578e8fafa7946d8cd83467729e
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Oct 26 21:14:34 2012 +0800
encoding/binary: ReadVarint reads a signed number, not unsigned number
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/6762051
変更の背景
このコミットの背景は、encoding/binary
パッケージの ReadVarint
関数のコメントが、その実際の動作と一致していなかったことです。ReadVarint
関数は、内部的に符号なし整数を読み取る ReadUvarint
を利用しつつ、結果として符号付き整数を正しくデコードするロジック(ジグザグエンコーディング)を含んでいました。しかし、既存のコメントは「符号なし整数を読み取る」と誤って記述されており、これが関数の意図や動作を誤解させる可能性がありました。
この変更は、コードの動作自体を変更するものではなく、ドキュメント(コメント)を修正することで、関数の真の目的と動作を正確に反映させ、開発者の混乱を防ぐことを目的としています。これは、コードの可読性と保守性を向上させるための重要なステップです。
前提知識の解説
1. 可変長整数 (Varint)
可変長整数(Varint)は、数値を効率的にエンコードするための手法です。特に、小さい数値は少ないバイト数で表現し、大きい数値はより多くのバイト数で表現することで、全体的なデータサイズを削減します。GoogleのProtocol Buffersなどで広く使用されています。
Varintの基本的な考え方は、各バイトの最上位ビット(MSB)を「継続ビット」として使用することです。MSBが1の場合、その数値は次のバイトにも続くことを示し、MSBが0の場合、そのバイトが数値の最後のバイトであることを示します。残りの7ビットが数値のデータとして使用されます。
2. 符号付き整数と符号なし整数
- 符号なし整数 (Unsigned Integer): 0以上の整数のみを表現します。すべてのビットが数値の大きさを表すために使用されます。
- 符号付き整数 (Signed Integer): 正の数、負の数、および0を表現します。通常、最上位ビットが符号ビットとして使用され、0は正、1は負を示します(2の補数表現が一般的)。
3. ジグザグエンコーディング (ZigZag Encoding)
Varintは通常、符号なし整数をエンコードするために設計されています。しかし、負の数をVarintで効率的に表現するためには、ジグザグエンコーディングが使用されます。
ジグザグエンコーディングは、符号付き整数を符号なし整数にマッピングする手法です。これにより、絶対値が小さい負の数(例: -1, -2)も、絶対値が小さい正の数(例: 1, 2)と同様に、Varintで少ないバイト数で表現できるようになります。
マッピングのルールは以下の通りです(例: 32ビット整数):
n
が正の場合:(n << 1)
n
が負の場合:(~n << 1) | 1
これにより、以下のようなマッピングが行われます。
- 0 -> 0
- -1 -> 1
- 1 -> 2
- -2 -> 3
- 2 -> 4
- ...
デコード時には、この逆の操作を行います。
ux
が偶数の場合:ux >> 1
ux
が奇数の場合:~(ux >> 1)
このコミットで修正された ReadVarint
関数は、まさにこのジグザグエンコーディングのデコードロジック(x := int64(ux >> 1)
と if ux&1 != 0 { x = ^x }
)を含んでいます。
技術的詳細
encoding/binary
パッケージの ReadVarint
関数は、io.ByteReader
からバイトを読み取り、それを int64
型の符号付き整数としてデコードします。この関数は、内部的に ReadUvarint
関数を呼び出して、まずバイト列を符号なしの uint64
として読み取ります。その後、読み取った uint64
の値に対してジグザグデコーディングを適用し、元の符号付き int64
の値に変換します。
ジグザグデコーディングのロジックは以下の通りです。
ux, err := ReadUvarint(r)
: まず、バイトリーダーr
から符号なし可変長整数ux
を読み取ります。x := int64(ux >> 1)
: 読み取ったux
を1ビット右シフトし、int64
にキャストします。これはジグザグエンコーディングのデコードの一部で、正の数と負の数の絶対値を復元するステップです。if ux&1 != 0 { x = ^x }
:ux
の最下位ビット(LSB)が1であるかどうかをチェックします。LSBが1の場合、元の数値は負の数であったことを示します。この場合、x
のビットを反転(1の補数)することで、元の負の数を復元します。
この一連の処理により、ReadVarint
は正しく符号付き整数をデコードします。したがって、以前のコメントが「符号なし整数を読み取る」と記述していたのは誤りであり、実際の動作は「符号付き整数を読み取る」ものでした。このコミットは、そのコメントの誤りを修正し、関数の振る舞いを正確に反映させるものです。
コアとなるコードの変更箇所
--- a/src/pkg/encoding/binary/varint.go
+++ b/src/pkg/encoding/binary/varint.go
@@ -123,7 +123,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) {
panic("unreachable")
}
-// ReadVarint reads an encoded unsigned integer from r and returns it as an int64.
+// ReadVarint reads an encoded signed integer from r and returns it as an int64.
func ReadVarint(r io.ByteReader) (int64, error) {
ux, err := ReadUvarint(r) // ok to continue in presence of error
x := int64(ux >> 1)
コアとなるコードの解説
変更されたのは、ReadVarint
関数の直前にあるコメント行のみです。
- // ReadVarint reads an encoded unsigned integer from r and returns it as an int64.
- これは変更前のコメントで、「符号なし整数を読み取る」と誤って記述されていました。
+ // ReadVarint reads an encoded signed integer from r and returns it as an int64.
- これは変更後のコメントで、「符号付き整数を読み取る」と修正されています。
この変更は、コードの実行ロジックには一切影響を与えません。ReadVarint
関数の実際の動作は、変更前も変更後も一貫して符号付き整数をデコードしていました。この修正は、関数のドキュメントとしてのコメントを、その実際の機能と一致させるためのものです。これにより、この関数を使用する開発者が、その目的と戻り値の型について正確な理解を得られるようになります。
関連リンク
- Go CL 6762051: https://golang.org/cl/6762051
参考にした情報源リンク
- Protocol Buffers - Encoding: https://developers.google.com/protocol-buffers/docs/encoding#varints
- GoDoc for encoding/binary: https://pkg.go.dev/encoding/binary
- ZigZag Encoding Explained: https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding
- Go source code for encoding/binary/varint.go (at the time of commit): (具体的なリンクはコミット時点のGoリポジトリの履歴を辿る必要がありますが、現在の最新版でも同様のロジックは確認できます)