[インデックス 14349] ファイルの概要
このコミットは、Go言語の標準ライブラリであるimage/jpeg
パッケージ内のJPEGデコーダが、JPEGストリーム中に存在する「フィルバイト (fill bytes)」を適切に処理するように修正するものです。具体的には、JPEGマーカーの前に挿入される可能性のある0xff
バイトをスキップするロジックが追加されました。
コミット
commit 64b3e590c00b9c1e532dd0014b16174c9397664c
Author: Nigel Tao <nigeltao@golang.org>
Date: Thu Nov 8 10:36:29 2012 +1100
image/jpeg: handle fill bytes.
Fixes #4337.
R=r, minux.ma
CC=golang-dev
https://golang.org/cl/6814098
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/64b3e590c00b9c1e532dd0014b16174c9397664c
元コミット内容
image/jpeg: handle fill bytes.
Fixes #4337.
このコミットは、Go言語のimage/jpeg
パッケージにおいて、JPEGデータストリーム内のフィルバイトを処理するように修正を加えるものです。これにより、GoのIssueトラッカーで報告されていた問題 #4337 が解決されます。
変更の背景
JPEGファイルフォーマットの仕様(特にITU-T Recommendation T.81 | ISO/IEC 10918-1)では、JPEGマーカーの前に任意の数の0xff
バイト(フィルバイト)を挿入することが許可されています。これは、データストリームの同期やアライメントのために使用されることがあります。
Go言語のimage/jpeg
パッケージの既存の実装では、このフィルバイトの存在が考慮されていませんでした。そのため、一部のJPEGファイル、特にマーカーの前に0xff
フィルバイトが含まれているファイルが正しくパースできず、エラーが発生する可能性がありました。この問題は、GoのIssue #4337として報告されており、このコミットはその問題を解決するために行われました。フィルバイトを適切にスキップすることで、より堅牢なJPEGデコーダが実現されます。
前提知識の解説
JPEGファイルフォーマットの基本
JPEG (Joint Photographic Experts Group) は、主に写真などの連続階調画像を圧縮するための標準的な画像フォーマットです。JPEGファイルは、複数の「セグメント」で構成されており、各セグメントは特定の情報(画像データ、メタデータ、圧縮パラメータなど)を含んでいます。
JPEGマーカー
JPEGファイル内の各セグメントは、2バイトの「マーカー」で始まります。マーカーは常に0xff
バイトで始まり、その後にセグメントの種類を示す1バイトが続きます。例えば、0xffd8
はSOI (Start Of Image) マーカー、0xffd9
はEOI (End Of Image) マーカーです。デコーダはこれらのマーカーを読み取ることで、データストリーム内のセグメントの境界を識別し、適切に処理を進めます。
フィルバイト (Fill Bytes)
JPEG標準のセクションB.1.1.2には、「任意のマーカーは、オプションで任意の数のフィルバイト(コードX'FF'が割り当てられたバイト)によって先行されてもよい」と記載されています。これは、マーカーの前に0xff
バイトが連続して出現する可能性があることを意味します。これらのフィルバイトは、データの内容には影響を与えず、デコーダはこれらを無視して次の有効なマーカーを探す必要があります。
Go言語のimage/jpeg
パッケージ
Go言語の標準ライブラリには、image/jpeg
パッケージが含まれており、JPEG画像のエンコードとデコードの機能を提供します。このパッケージは、image.Image
インターフェースを実装しており、他の画像フォーマット(PNG, GIFなど)と同様に統一された方法で画像を扱うことができます。内部的には、JPEGのデータストリームを読み込み、マーカーを解析し、圧縮された画像データをデコードしてピクセルデータに変換するロジックが含まれています。
技術的詳細
このコミットは、src/pkg/image/jpeg/reader.go
ファイル内のdecoder
構造体のdecode
メソッドに修正を加えています。decode
メソッドは、io.Reader
からJPEGデータストリームを読み込み、画像をデコードする主要な関数です。
既存のコードでは、マーカーを読み取る際に、最初の0xff
バイトの後に続く1バイトを直接マーカーとして解釈していました。しかし、JPEG標準で許可されているフィルバイトの存在を考慮していなかったため、0xff
が連続して出現した場合に、最初の0xff
をマーカーの開始バイトとして認識し、その次の0xff
をマーカーの種類を示すバイトとして誤って解釈してしまう可能性がありました。これにより、不正なマーカーとしてエラーになったり、後続のパース処理が失敗したりする原因となっていました。
このコミットでは、マーカーを読み取る前に、連続する0xff
バイトをスキップするためのfor
ループが追加されました。このループは、d.r.ReadByte()
を使用して1バイトずつ読み込み、読み込んだバイトが0xff
である限りループを続けます。これにより、実際のマーカーバイト(0xff
の後に続く、0xff
ではないバイト)が確実にmarker
変数に格納されるようになります。
エラーハンドリングも適切に行われており、バイトの読み込み中にエラーが発生した場合は、そのエラーが呼び出し元に返されます。
コアとなるコードの変更箇所
変更はsrc/pkg/image/jpeg/reader.go
ファイルのdecode
関数内、既存のマーカー読み取りロジックの直前に追加されています。
--- a/src/pkg/image/jpeg/reader.go
+++ b/src/pkg/image/jpeg/reader.go
@@ -236,6 +236,14 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
return nil, FormatError("missing 0xff marker start")
}
marker := d.tmp[1]
+ for marker == 0xff {
+ // Section B.1.1.2 says, "Any marker may optionally be preceded by any
+ // number of fill bytes, which are bytes assigned code X'FF'".
+ marker, err = d.r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+ }
if marker == eoiMarker { // End Of Image.
break
}
コアとなるコードの解説
追加されたコードブロックは以下の通りです。
for marker == 0xff {
// Section B.1.1.2 says, "Any marker may optionally be preceded by any
// number of fill bytes, which are bytes assigned code X'FF'".
marker, err = d.r.ReadByte()
if err != nil {
return nil, err
}
}
for marker == 0xff
: このループは、現在読み取られたmarker
バイトが0xff
である限り継続します。初期のmarker
は、d.tmp[1]
から取得されたもので、これは0xff
マーカー開始バイトの直後に続くバイトです。もしこのバイトが0xff
であれば、それはフィルバイトであると判断されます。marker, err = d.r.ReadByte()
:d.r
はio.Reader
インターフェースを実装しており、ここから次の1バイトを読み込みます。読み込んだバイトは新しいmarker
の値として格納されます。if err != nil
: バイトの読み込み中にエラーが発生した場合(例: EOFに達した、I/Oエラーなど)、即座にエラーを返してデコード処理を中断します。これは堅牢なエラーハンドリングのために重要です。- ループの継続: 新しく読み込んだ
marker
が再び0xff
であれば、ループは継続し、さらに次のバイトを読み込みます。これにより、連続するフィルバイトがすべてスキップされます。 - ループの終了:
marker
が0xff
以外の値になった時点でループは終了します。このときmarker
には、実際のJPEGマーカーの種類を示すバイトが格納されており、後続の処理で正しく解釈されるようになります。
この変更により、JPEGデコーダはJPEG標準に完全に準拠し、フィルバイトを含むJPEGファイルを問題なく処理できるようになりました。
関連リンク
- Go言語のIssue #4337: このコミットが解決した具体的な問題に関する情報がGoのIssueトラッカーに存在します。
- Go言語の
image/jpeg
パッケージのドキュメント: https://pkg.go.dev/image/jpeg - JPEG標準 (ITU-T Recommendation T.81 | ISO/IEC 10918-1): JPEGフォーマットの公式仕様書。フィルバイトに関する記述はセクションB.1.1.2にあります。
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commit/64b3e590c00b9c1e532dd0014b16174c9397664c
- Go言語のコードレビューシステム (Gerrit): https://golang.org/cl/6814098 (コミットメッセージに記載されているChange-ID)
- JPEGファイルフォーマットに関する一般的な情報源(例: Wikipedia, 各種技術ブログなど)
- Go言語の
image/jpeg
パッケージのソースコード