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

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

このコミットは、Go言語の標準ライブラリencoding/base32パッケージにおいて、Base32デコード時に改行文字(\rおよび\n)を無視するように変更を加えるものです。これにより、Base32でエンコードされたデータが改行を含む場合でも、正しくデコードできるようになります。

コミット

commit 107b0f12bc80967e7133bb971e818faed08f5274
Author: David Symonds <dsymonds@golang.org>
Date:   Fri Feb 3 13:36:38 2012 +1100

    encoding/base32: ignore new line characters during decode.
    
    This is the analogue to the encoding/base64 change,
    https://golang.org/cl/5610045.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5617056

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

https://github.com/golang/go/commit/107b0f12bc80967e7133bb971e818faed08f5274

元コミット内容

encoding/base32: ignore new line characters during decode.

このコミットは、Base32デコード時に改行文字を無視するようにします。これは、encoding/base64パッケージにおける同様の変更(https://golang.org/cl/5610045)に対応するものです。

変更の背景

Base32エンコーディングは、バイナリデータをASCII文字列に変換する手法の一つです。これは、特にテキストベースのプロトコルやシステムでバイナリデータを安全に転送・保存する際に利用されます。しかし、エンコードされたデータがファイルやネットワーク経由で転送される際、途中で改行文字が挿入されることがあります。例えば、メールシステムでは行の長さに制限があるため、長いBase32文字列が自動的に折り返され、改行が挿入されることがあります。

このコミット以前のencoding/base32パッケージのデコーダは、このような改行文字を不正な文字として扱い、デコードエラーを発生させていました。これは、Base32でエンコードされたデータが改行を含む場合に、デコードが失敗するという問題を引き起こしていました。

同様の問題はencoding/base64パッケージでも存在し、先行して改行文字を無視する変更が導入されました(https://golang.org/cl/5610045)。このコミットは、Base32デコーダにも同様の堅牢性を持たせることを目的としています。改行文字を無視することで、より幅広い入力形式に対応し、デコード処理の信頼性を向上させることができます。

前提知識の解説

Base32エンコーディング

Base32は、RFC 4648で定義されているデータエンコーディング方式の一つです。これは、バイナリデータを32種類のASCII文字(A-Zと2-7)とパディング文字(=)を使用して表現します。

  • エンコードの仕組み:

    • 入力バイナリデータを5ビットのグループに分割します。
    • 各5ビットのグループを、Base32アルファベットの1文字に対応させます。
    • 入力データのビット数が5の倍数でない場合、パディング文字=を使用して出力文字列の長さを8の倍数にします。
    • Base32は、5バイト(40ビット)の入力データを8文字(40ビット)の出力に変換します。
  • Base32の利点:

    • Base64と比較して、出力文字列が長くなりますが、大文字・小文字の区別がなく、数字も少ないため、手動での入力や読み取りが容易です。
    • ファイルシステム名やURLなど、特定の文字セットしか許容しない環境での利用に適しています。

改行文字

コンピュータシステムにおいて、改行文字はテキストの行の終わりを示す特殊な文字です。主な改行文字には以下の2種類があります。

  • LF (Line Feed, \n): Unix系システムで主に使われる改行コード。
  • CR (Carriage Return, \r): 古いMacシステムや、CRLFの一部として使われる改行コード。
  • CRLF (Carriage Return + Line Feed, \r\n): Windowsシステムやインターネットプロトコル(HTTP, SMTPなど)で使われる改行コード。

これらの改行文字は、テキストデータの整形や表示に用いられますが、エンコードされたバイナリデータの一部として解釈されると、デコードエラーの原因となることがあります。

技術的詳細

この変更の核心は、encoding/base32パッケージのdecodeメソッドにおける入力処理の改善です。以前のdecodeメソッドは、入力バイト列を固定長(8バイト)のブロックとして処理し、その中に改行文字が含まれていると、CorruptInputErrorを返していました。

新しい実装では、入力バイト列を1バイトずつ読み込み、それが改行文字(\rまたは\n)である場合は単にスキップするように変更されています。これにより、デコーダは改行文字を無視し、有効なBase32文字のみを処理対象とすることができます。

具体的には、decodeメソッド内のループが変更され、srcスライスから1バイトずつ読み込む際に、改行文字のチェックが追加されました。

  • 変更前: srcスライスを8バイトの固定ブロックとして処理し、インデックス計算を行っていました。
  • 変更後: srcスライスを先頭から1バイトずつ処理し、\rまたは\nであればスキップし、それ以外の文字であればデコードマップで変換します。これにより、入力ストリームから改行文字が透過的に除去されます。

また、パディング文字=の処理も改善され、改行文字がパディングの途中に挿入されても正しく処理されるようになりました。エラー発生時のCorruptInputErrorのオフセット計算も、改行文字をスキップした後の実際の文字位置を反映するように修正されています。

さらに、Decodeメソッドから、入力の長さが8の倍数であるという前提条件が削除されました。これは、改行文字が挿入されることで入力の長さが8の倍数でなくなる可能性があるため、より柔軟な入力に対応するためです。

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

src/pkg/encoding/base32/base32.go

  • func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) メソッドの変更:
    • 入力srcの処理方法が、固定長ブロックから1バイトずつの読み込みに変更されました。
    • for j := 0; j < 8; { ... } ループ内で、in := src[0]; src = src[1:] のように1バイトずつ読み込み、if in == '\r' || in == '\n' { continue } で改行文字をスキップするロジックが追加されました。
    • パディング文字=の処理ロジックが、改行文字のスキップに対応するように修正されました。
    • CorruptInputErrorのオフセット計算が、スキップされた文字を考慮するように修正されました。
    • dstへの書き込みインデックス計算が、i*5+Xからdst[X]のように相対的なものに変更され、dst = dst[5:]でスライスを進めるようになりました。
  • func (enc *Encoding) Decode(dst, src []byte) (n int, err error) メソッドの変更:
    • if len(src)%8 != 0 { ... } という、入力の長さが8の倍数であることをチェックする行が削除されました。
    • コメントに「New line characters (\r and \n) are ignored.」が追加されました。

src/pkg/encoding/base32/base32_test.go

  • TestDecode関数内のtestEqualの引数が修正されました。
  • TestNewLineCharactersという新しいテスト関数が追加されました。
    • このテスト関数は、改行文字を含む様々なBase32エンコード文字列が、正しくデコードされて同じ結果("sure")になることを検証します。これにより、改行文字無視の機能が期待通りに動作することを確認しています。

コアとなるコードの解説

base32.godecode メソッドの変更点

変更の最も重要な部分は、decodeメソッド内の入力処理ループです。

変更前(抜粋):

func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
	for i := 0; i < len(src)/8 && !end; i++ {
		// ...
		for j := 0; j < 8; j++ {
			in := src[i*8+j] // 固定インデックスでアクセス
			// ...
		}
		// ...
	}
	// ...
}

このコードでは、srcスライスを8バイトのブロックに分割し、i*8+jというインデックスで直接アクセスしていました。このため、ブロックの途中に改行文字があると、それが不正な文字として扱われました。

変更後(抜粋):

func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
	osrc := src // 元のsrcを保存し、エラーオフセット計算に使用
	for len(src) > 0 && !end { // srcが空になるまでループ
		// ...
		for j := 0; j < 8; { // jは有効なBase32文字のカウント
			if len(src) == 0 {
				// 入力が途中で尽きた場合のエラー処理
				return n, false, CorruptInputError(len(osrc) - len(src) - j)
			}
			in := src[0] // srcの先頭から1バイト読み込み
			src = src[1:] // srcスライスを1バイト進める

			if in == '\r' || in == '\n' {
				// 改行文字であればスキップし、次の文字へ
				continue
			}
			// ... 有効なBase32文字の処理 ...
			dbuf[j] = enc.decodeMap[in]
			if dbuf[j] == 0xFF {
				// 不正な文字の場合のエラー処理
				return n, false, CorruptInputError(len(osrc) - len(src) - 1)
			}
			j++ // 有効なBase32文字を処理した場合のみjをインクリメント
		}
		// ... 5バイトへのパック処理 ...
		dst = dst[5:] // dstスライスを5バイト進める
	}
	// ...
}

この変更により、decodeメソッドは入力ストリームから改行文字を透過的に除去し、有効なBase32文字のみをデコード処理に渡すようになりました。osrc変数は、エラー発生時に元の入力文字列からの正確なオフセットを計算するために導入されました。

base32_test.goTestNewLineCharacters

この新しいテストケースは、改行文字を無視する機能が正しく実装されていることを確認するために非常に重要です。

func TestNewLineCharacters(t *testing.T) {
	const expected = "sure"
	examples := []string{
		"ON2XEZI=",
		"ON2XEZI=\r",
		"ON2XEZI=\n",
		"ON2XEZI=\r\n",
		"ON2XEZ\r\nI=",
		"ON2X\rEZ\nI=",
		"ON2X\nEZ\rI=",
		"ON2XEZ\nI=",
		"ON2XEZI\n=",
	}
	for _, e := range examples {
		buf, err := StdEncoding.DecodeString(e)
		if err != nil {
			t.Errorf("Decode(%q) failed: %v", e, err)
			continue
		}
		if s := string(buf); s != expected {
			t.Errorf("Decode(%q) = %q, want %q", e, s, expected)
		}
	}
}

このテストでは、"sure"という文字列をBase32エンコードした結果("ON2XEZI=")に、様々な位置で改行文字を挿入した複数のパターンを定義しています。それぞれのパターンがエラーなく、かつ正しく"sure"にデコードされることを検証することで、改行文字無視の堅牢性が保証されます。

関連リンク

  • Base64の類似変更: https://golang.org/cl/5610045
    • このコミットの背景で言及されている、encoding/base64パッケージにおける改行文字無視の変更です。Base32の変更は、これに倣ったものです。
  • このコミットのGerritレビュー: https://golang.org/cl/5617056
    • Goプロジェクトでは、Gerritというコードレビューシステムが使われています。このリンクは、このコミットがレビューされた際のGerritのページです。

参考にした情報源リンク