[インデックス 1351] ファイルの概要
このコミットは、Go言語の初期のio
パッケージ内のByteBuffer
型に関するバグ修正です。具体的には、src/lib/io/bytebuffer.go
ファイルが変更されています。
コミット
commit a10267adcdd40093cb2c9d8a470194332b947b86
Author: Rob Pike <r@golang.org>
Date: Tue Dec 16 13:01:39 2008 -0800
If ByteBuffer has never been used, b.buf is nil but Data() should still work.
Fix the bug using a (safe) shared global empty array.
R=rsc
DELTA=8 (8 added, 0 deleted, 0 changed)
OCL=21303
CL=21303
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a10267adcdd40093cb2c9d8a470194332b947b86
元コミット内容
ByteBuffer
が一度も使用されていない場合、b.buf
はnil
ですが、Data()
メソッドはそれでも機能するべきです。このバグを、(安全な)共有グローバル空配列を使用して修正します。
変更の背景
Go言語の初期のByteBuffer
実装において、新しいByteBuffer
インスタンスが作成された直後、つまりデータが一度も書き込まれていない状態では、内部のバッファ(b.buf
)がnil
になっていました。この状態でData()
メソッドを呼び出すと、nil
スライスに対してスライス操作を行おうとするため、ランタイムパニックや予期せぬ動作を引き起こす可能性がありました。
Data()
メソッドの期待される振る舞いは、バッファが空であっても、有効な(ただし長さが0の)バイトスライスを返すことです。これは、Go言語におけるスライスの慣習、特にbytes.Buffer
のような標準ライブラリの振る舞いと一致させるためにも重要でした。nil
スライスと空スライスは異なる概念であり、多くのGoのAPIは空の状態を表すためにnil
ではなく空スライスを返します。
このコミットは、この初期化時のnil
バッファの問題を解決し、ByteBuffer
が常に堅牢な振る舞いをするようにするためのものです。
前提知識の解説
Go言語におけるスライスとnil
Go言語のスライスは、配列の一部を参照する軽量なデータ構造です。スライスは、ポインタ、長さ(len
)、容量(cap
)の3つの要素で構成されます。
nil
スライス: スライスのゼロ値はnil
です。nil
スライスは、基になる配列を持たず、長さも容量も0です。var s []int
のように宣言されたスライスはnil
スライスになります。nil
スライスは、有効なスライスであり、多くの操作(len(s)
、cap(s)
、for range s
など)を安全に行うことができます。- 空スライス: 長さが0のスライスは空スライスと呼ばれます。これは
make([]int, 0)
や[]int{}
のように作成できます。空スライスは基になる配列を持つ場合と持たない場合がありますが、長さは常に0です。
重要な違いは、nil
スライスは「存在しない」ことを意味するのに対し、空スライスは「存在するが、要素がない」ことを意味するという点です。多くのGoの関数やメソッドは、結果がない場合にnil
ではなく空スライスを返します。これは、呼び出し側がnil
チェックをせずにループ処理などを行えるようにするためです。
ByteBuffer
(Go言語初期のI/Oバッファ)
このコミットが対象としているByteBuffer
は、Go言語の初期に存在したI/Oバッファリングのための型です。現在のGoの標準ライブラリでは、同様の機能は主にbytes.Buffer
型によって提供されています。ByteBuffer
は、バイトデータを効率的に読み書きするための内部バッファを管理していました。
ByteBuffer
の内部には、バイトデータを保持するためのスライス(このコミットではb.buf
と記述されている)と、読み書きのオフセットや長さを示すフィールドがありました。
技術的詳細
このバグは、ByteBuffer
が初期化された直後、つまりWrite
メソッドなどでデータが一度も追加されていない状態では、内部のb.buf
フィールドがnil
であることに起因していました。Data()
メソッドは、このb.buf
スライスに対してスライス操作(b.buf[b.off:b.len]
)を行っていましたが、nil
スライスに対してスライス操作を行うとランタイムパニックが発生します。
修正の核心は、Data()
メソッドが呼び出された際にb.buf
がnil
であるかどうかをチェックし、もしnil
であれば、代わりに共有のグローバルな空バイトスライスを返すようにすることです。
導入されたEmptyByteArray
は、new([]byte, 0)
によって作成されます。これは、長さと容量が0の新しいバイトスライスへのポインタを返します。このスライス自体は不変(immutable)であり、共有しても安全です。なぜなら、このスライスを介してデータを追加したり変更したりすることはできないため、複数のByteBuffer
インスタンスが同じ空スライスを参照しても競合状態が発生しないからです。
このアプローチにより、ByteBuffer
が初期状態であってもData()
メソッドは常に有効な(長さ0の)スライスを返し、呼び出し側はnil
チェックをすることなく、返されたスライスを安全に扱うことができるようになります。これは、Go言語のイディオムに沿った堅牢な設計です。
コアとなるコードの変更箇所
--- a/src/lib/io/bytebuffer.go
+++ b/src/lib/io/bytebuffer.go
@@ -75,7 +75,15 @@ func (b *ByteBuffer) Len() int {
return b.len
}
+// If the buffer is empty, Data() should still give a valid array.
+// Use this variable as a surrogate. It's immutable (can't be
+// grown, can't store any data) so it's safe to share.
+var EmptyByteArray = new([]byte, 0)
+
func (b *ByteBuffer) Data() *[]byte {
+ if b.buf == nil {
+ return EmptyByteArray
+ }
return b.buf[b.off:b.len]
}
コアとなるコードの解説
-
var EmptyByteArray = new([]byte, 0)
:EmptyByteArray
というグローバル変数が導入されました。new([]byte, 0)
は、長さと容量が0の新しいバイトスライスを指すポインタを作成します。このスライス自体は、要素を持たないため、不変(immutable)と見なすことができます。- この変数は、
ByteBuffer
が空で内部バッファがnil
の場合にData()
メソッドが返す「代替」の空スライスとして機能します。グローバルに共有することで、メモリの無駄を省き、毎回新しい空スライスを作成するオーバーヘッドを避けています。
-
func (b *ByteBuffer) Data() *[]byte { ... }
:Data()
メソッドの冒頭にif b.buf == nil
という条件分岐が追加されました。- これは、
ByteBuffer
の内部バッファb.buf
がnil
であるかどうかをチェックします。b.buf
がnil
であるのは、ByteBuffer
が初期化されたばかりで、まだデータが書き込まれていない状態です。 - もし
b.buf
がnil
であれば、return EmptyByteArray
が実行され、事前に定義されたグローバルな空バイトスライスが返されます。これにより、nil
スライスに対する不正な操作を防ぎます。 b.buf
がnil
でない場合(つまり、データが書き込まれているか、少なくともバッファが初期化されている場合)は、以前と同様にreturn b.buf[b.off:b.len]
が実行され、内部バッファの適切な部分がスライスとして返されます。
この変更により、ByteBuffer
のData()
メソッドは、バッファが空の状態であっても常に有効な(長さ0の)バイトスライスを返すようになり、より堅牢で予測可能な振る舞いをするようになりました。
関連リンク
参考にした情報源リンク
- コミットデータ:
/home/orange/Project/comemo/commit_data/1351.txt
- Go言語におけるスライスと
nil
の概念に関する一般的な知識 - Go言語の
bytes.Buffer
の振る舞いに関する一般的な知識 - Web検索: "Go ByteBuffer nil Data() early implementation" (Go言語の初期の
ByteBuffer
実装におけるnil
の扱いに関する情報収集)