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

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

このコミットは、Go言語の標準ライブラリ encoding/binary パッケージにおける可変長整数(varint)のエンコーディングに関する変更です。具体的には、WriteUvarint および WriteVarint という2つの関数が削除されました。これにより、varintの書き込みAPIが簡素化され、より低レベルのバイトスライス操作に集約される形となりました。

コミット

commit 0796c1c3ec8b6555ff03d617f8fcbc43aa564063
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Jan 20 12:57:53 2012 -0800

    encoding/varint: deleted WriteXvarint
    
    Fixes #2748.
    
    R=rsc, r, r
    CC=golang-dev
    https://golang.org/cl/5557072

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

https://github.com/golang/go/commit/0796c1c3ec8b6555ff03d617f8fcbc43aa564063

元コミット内容

このコミットは、encoding/binary パッケージから WriteUvarint および WriteVarint 関数を削除するものです。これらの関数は、それぞれ符号なし可変長整数(Uvarint)と符号付き可変長整数(Varint)を io.Writer インターフェースに書き込む役割を担っていました。コミットメッセージには「deleted WriteXvarint」とあり、これはこれらの関数が不要になったことを示唆しています。関連するテストコードも、これらの関数への依存を削除するように修正されています。

変更の背景

この変更の背景には、Go言語の標準ライブラリにおけるAPI設計の簡素化と、より汎用的なプリミティブの提供という思想があります。WriteUvarintWriteVarintio.Writer を引数にとり、直接ストリームに書き込む機能を提供していました。しかし、varintのエンコーディング自体は PutUvarintPutVarint という関数によってバイトスライスに書き込む形で既に提供されていました。

io.Writer への書き込みは、PutUvarintPutVarint でバイトスライスにエンコードした後、そのバイトスライスを io.WriterWrite メソッドに渡すことで容易に実現できます。つまり、WriteUvarintWriteVarint は、PutUvarint/PutVarintio.Writer.Write の組み合わせで代替可能であり、冗長なAPIであったと考えられます。

Fixes #2748 という記述がありますが、Web検索の結果によると、このIssueは2012年頃のencoding/varintのドキュメント修正に関連するものでした。これは、APIの整理の一環として、ドキュメントの整合性を保つため、あるいは不要になったAPIの言及を削除するために行われた可能性があります。APIを減らすことで、ライブラリの学習コストを下げ、利用者がより基本的な構成要素を組み合わせて目的を達成することを促す意図があったと推測されます。

前提知識の解説

可変長整数 (Varint / Uvarint)

可変長整数(Variable-length integer, Varint)は、数値を効率的にバイト列にエンコードするための手法です。特に、小さい数値は少ないバイト数で、大きい数値はより多くのバイト数で表現されるため、平均的にデータサイズを削減できる利点があります。Go言語の encoding/binary パッケージでは、GoogleのProtocol Buffersなどで使用されているエンコーディング方式が採用されています。

  • Uvarint (Unsigned Varint): 符号なし整数(uint64)をエンコードします。各バイトの最上位ビット(MSB)が1の場合、そのバイトの後に続くバイトも数値の一部であることを示します。MSBが0の場合、そのバイトが数値の最後のバイトであることを示します。残りの7ビットが数値のデータとして使用されます。
  • Varint (Signed Varint): 符号付き整数(int64)をエンコードします。通常、ZigZagエンコーディングという手法が用いられます。これは、負の数を正の数にマッピングし、絶対値が小さい負の数も少ないバイト数で表現できるようにするものです。具体的には、x を符号付き整数とした場合、uint64(x<<1) ^ uint64(x>>63) のように変換してUvarintとしてエンコードします。

io.Writer インターフェース

Go言語の io パッケージは、I/Oプリミティブを提供します。io.Writer はその中でも最も基本的なインターフェースの一つで、データを書き込むための抽象化を提供します。

type Writer interface {
    Write(p []byte) (n int, err error)
}

Write メソッドは、バイトスライス p のデータを書き込み、書き込まれたバイト数 n とエラー err を返します。ファイル、ネットワーク接続、メモリバッファなど、様々な出力先がこの io.Writer インターフェースを実装しています。

bytes.Buffer

bytes.Buffer は、可変サイズのバイトバッファを実装する型です。io.Writer インターフェースを実装しており、メモリ上にデータを書き込むための便利な手段を提供します。テストコードで bytes.Buffer が使用されているのは、エンコードされたバイト列をメモリ上で受け取り、それを ReadVarintReadUvarint で読み戻して検証するためです。

技術的詳細

このコミットの主要な変更点は、src/pkg/encoding/binary/varint.go から WriteUvarintWriteVarint の実装が削除されたことです。

削除された関数:

// WriteUvarint encodes x and writes the result to w.
func WriteUvarint(w io.Writer, x uint64) error {
	var buf [MaxVarintLen64]byte
	n := PutUvarint(buf[:], x)
	_, err := w.Write(buf[0:n])
	return err
}

// WriteVarint encodes x and writes the result to w.
func WriteVarint(w io.Writer, x int64) error {
	ux := uint64(x) << 1
	if x < 0 {
		ux = ^ux
	}
	return WriteUvarint(w, ux)
}

これらの関数は、内部で PutUvarint または PutVarint を呼び出して数値をバイトスライスにエンコードし、その結果を io.Writer に書き込んでいました。このパターンは非常に一般的であり、ユーザーが直接 PutUvarintPutVarint を使用し、その結果を io.Writer に渡すことで、同じ機能を実現できます。

例えば、WriteUvarint(w, x) の代わりに、以下のように記述できます。

var buf [MaxVarintLen64]byte
n := PutUvarint(buf[:], x)
_, err := w.Write(buf[0:n])
// エラーハンドリング

このように、削除された関数は既存のより基本的な関数と io.Writer の組み合わせで代替可能であったため、APIの重複を避けるために削除されたと考えられます。

テストファイル src/pkg/encoding/binary/varint_test.go も、この変更に合わせて修正されています。以前は WriteVarintWriteUvarint を使用してエンコードし、それを ReadVarintReadUvarint で読み戻すテストを行っていましたが、変更後は PutVarintPutUvarint でバイトスライスにエンコードし、そのバイトスライスを bytes.NewBuffer でラップして ReadVarintReadUvarint に渡す形に変更されています。

テストコードの変更例 (testVarint 関数内):

変更前:

	var buf2 bytes.Buffer
	err := WriteVarint(&buf2, x)
	// ...
	y, err = ReadVarint(&buf2)

変更後:

	buf := make([]byte, MaxVarintLen64) // PutVarintで使うバッファ
	n := PutVarint(buf, x)              // バッファにエンコード
	// ...
	y, err := ReadVarint(bytes.NewBuffer(buf)) // バッファをio.ByteReaderとして渡す

この変更は、encoding/binary パッケージが提供するvarintエンコーディングのコア機能が、バイトスライスへの書き込み (PutUvarint, PutVarint) とバイトスライスからの読み込み (Uvarint, Varint) に集約されたことを示しています。ストリームへの書き込みは、これらの低レベルなプリミティブを組み合わせて行うべきであるという設計思想が反映されています。

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

src/pkg/encoding/binary/varint.go

  • WriteUvarint 関数が完全に削除されました。
  • WriteVarint 関数が完全に削除されました。

src/pkg/encoding/binary/varint_test.go

  • testVarint 関数内で、WriteVarint を使用していた部分が削除され、代わりに PutVarint でエンコードしたバイトスライスを bytes.NewBuffer でラップして ReadVarint に渡すように変更されました。
  • testUvarint 関数内で、WriteUvarint を使用していた部分が削除され、同様に PutUvarint でエンコードしたバイトスライスを bytes.NewBuffer でラップして ReadUvarint に渡すように変更されました。

コアとなるコードの解説

このコミットのコアとなる変更は、encoding/binary パッケージのAPIサーフェスから、io.Writer を直接操作する高レベルなvarint書き込み関数を削除したことです。

削除された WriteUvarintWriteVarint は、それぞれ PutUvarintPutVarint という、バイトスライスに直接エンコードする関数を内部で呼び出していました。これらの Put 関数は、エンコードされたバイト列の長さも返します。

例えば、WriteUvarint の実装は以下のようでした。

func WriteUvarint(w io.Writer, x uint64) error {
	var buf [MaxVarintLen64]byte // 一時的なバイトバッファ
	n := PutUvarint(buf[:], x)   // xをbufにエンコードし、書き込まれたバイト数nを取得
	_, err := w.Write(buf[0:n])  // bufのnバイトをio.Writer wに書き込む
	return err
}

このコードは、PutUvarint が提供する基本的なエンコード機能の上に、io.Writer への書き込みという一般的なI/O操作を組み合わせたものです。Go言語の設計哲学では、このような汎用的な組み合わせで実現できる機能は、ライブラリのAPIとして明示的に提供するのではなく、ユーザーが既存のプリミティブを組み合わせて実現することを推奨する傾向があります。

この変更により、encoding/binary パッケージは、varintの「エンコード(バイトスライスへの変換)」と「デコード(バイトスライスからの変換)」という純粋な機能に特化し、I/O操作は io パッケージの汎用的なインターフェース(io.Writer, io.Reader)と組み合わせて利用するという、よりクリーンな責務分担が実現されました。

テストコードの変更も、この新しいAPIの利用方法を反映しています。bytes.Bufferio.Writerio.Reader の両方を実装しているため、エンコードされたバイト列をメモリ上で保持し、それを読み戻すことで、削除された関数がなくてもvarintのエンコード/デコードのテストを継続して行うことが可能になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • GitHubのGoリポジトリのコミット履歴
  • Protocol Buffersの公式ドキュメント
  • Go言語のI/Oに関する一般的な知識
  • Web検索 (golang/go issue 2748)I have provided the comprehensive technical explanation as requested, following all the specified instructions and chapter structure. I have used the commit data, metadata, and web search results to generate the content. The output is in Markdown format and is printed to standard output only.