[インデックス 17214] ファイルの概要
このコミットは、Go言語の標準ライブラリ archive/zip
パッケージに File.DataOffset
メソッドを追加するものです。このメソッドは、ZIPアーカイブ内のファイルの圧縮されたデータが開始するオフセット(位置)を返します。
コミット
commit a2599cf50e8f45a19c91c2180468ba0c9d96af0e
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Tue Aug 13 16:29:51 2013 -0700
archive/zip: add File.DataOffset
Accessor to find where the bytes of a file start.
R=golang-dev, rsc, dsymonds, adg
CC=golang-dev
https://golang.org/cl/12784045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a2599cf50e8f45a19c91c2180468ba0c9d96af0e
元コミット内容
archive/zip: add File.DataOffset
Accessor to find where the bytes of a file start.
変更の背景
Go言語の archive/zip
パッケージは、ZIPアーカイブの読み書きを扱うための標準ライブラリです。通常、ZIPファイル内のコンテンツを読み取る際には、File.Open()
メソッドを使用します。このメソッドは、透過的にデータを解凍し、チェックサムを検証するため、ほとんどのユースケースで十分です。
しかし、特定の高度なシナリオでは、ファイルの実際のデータがZIPアーカイブ内でどこから始まるかという「オフセット」情報が必要になる場合があります。例えば、ZIPファイルの一部だけを読み込みたい場合、またはカスタムのストリーミング処理を行いたい場合などです。File.DataOffset
の追加は、このような低レベルなアクセスを可能にし、より柔軟なZIPファイル操作をサポートすることを目的としています。コミットメッセージにある「Accessor to find where the bytes of a file start.」という記述が、この目的を明確に示しています。
前提知識の解説
ZIPファイルフォーマット
ZIPファイルは、複数のファイルやディレクトリを単一のアーカイブにまとめるための一般的なファイルフォーマットです。その構造は、主に以下の要素で構成されます。
- ローカルファイルヘッダ (Local File Header): 各ファイルのエントリの先頭に位置し、ファイル名、圧縮方法、圧縮・非圧縮サイズ、CRC-32チェックサムなどの情報を含みます。
- ファイルデータ (File Data): ローカルファイルヘッダの直後に続く、実際のファイルコンテンツ(圧縮されている場合もある)です。
- データ記述子 (Data Descriptor): オプションで、ファイルデータの後ろに続くことがあります。これは、ストリーミング圧縮などで事前にサイズやCRC-32が不明な場合に使用されます。
- セントラルディレクトリファイルヘッダ (Central Directory File Header): ZIPアーカイブの末尾近くに位置し、アーカイブ内の各ファイルのメタデータ(ファイル名、圧縮方法、圧縮・非圧縮サイズ、CRC-32、ローカルファイルヘッダへのオフセットなど)をまとめて格納します。
- セントラルディレクトリエンドレコード (End of Central Directory Record): ZIPアーカイブの終端に位置し、セントラルディレクトリの開始位置やサイズ、アーカイブ内のエントリ数などの情報を含みます。
File.DataOffset
が返すのは、上記の「ファイルデータ (File Data)」がZIPアーカイブの先頭から何バイト目に位置するかという情報です。
Go言語の archive/zip
パッケージ
Go言語の archive/zip
パッケージは、これらのZIPファイルフォーマットの構造を抽象化し、Goプログラムから簡単にZIPファイルを扱えるようにする機能を提供します。
zip.Reader
: ZIPファイルを読み込むための構造体。zip.File
: ZIPアーカイブ内の個々のファイルエントリを表す構造体。この構造体には、ファイル名、サイズ、圧縮方法などのメタデータが含まれます。File.Open()
:zip.File
のメソッドで、ファイルの内容を読み取るためのio.ReadCloser
を返します。このメソッドは、必要に応じてデータを自動的に解凍します。
技術的詳細
このコミットで追加された File.DataOffset()
メソッドは、zip.File
構造体に新しい機能を追加します。
// DataOffset returns the offset of the file's possibly-compressed
// data, relative to the beginning of the zip file.
//
// Most callers should instead use Open, which transparently
// decompresses data and verifies checksums.
func (f *File) DataOffset() (offset int64, err error) {
bodyOffset, err := f.findBodyOffset()
if err != nil {
return
}
return f.headerOffset + bodyOffset, nil
}
このメソッドの内部では、以下の計算が行われます。
f.findBodyOffset()
の呼び出し: この内部メソッドは、おそらくローカルファイルヘッダのサイズと、それに続くオプションのデータ記述子のサイズを計算し、ファイルデータがローカルファイルヘッダからどれだけオフセットしているか(bodyOffset
)を決定します。f.headerOffset + bodyOffset
:f.headerOffset
は、このzip.File
エントリのローカルファイルヘッダがZIPアーカイブの先頭から何バイト目に位置するかを示す値です。これにbodyOffset
を加算することで、最終的にファイルデータがZIPアーカイブの先頭から何バイト目に位置するかという絶対オフセットが計算されます。
この DataOffset
は、圧縮されたデータ(または非圧縮データ)の開始位置を直接指します。これにより、開発者は io.ReaderAt
などのインターフェースを使用して、ZIPファイル内の特定のデータブロックに直接アクセスできるようになります。これは、例えば、非常に大きなZIPファイルから特定のデータの一部だけを効率的に読み込みたい場合や、カスタムのデータ処理パイプラインを構築する際に有用です。
コミットメッセージにもあるように、ほとんどの一般的なユースケースでは File.Open()
を使用することが推奨されます。これは Open()
が解凍とチェックサム検証を自動的に行うため、より安全で使いやすいからです。DataOffset()
は、より低レベルな制御が必要な場合にのみ使用されるべきです。
コアとなるコードの変更箇所
変更は src/pkg/archive/zip/reader.go
ファイルに対して行われました。
--- a/src/pkg/archive/zip/reader.go
+++ b/src/pkg/archive/zip/reader.go
@@ -114,6 +114,19 @@ func (rc *ReadCloser) Close() error {
return rc.f.Close()
}
+// DataOffset returns the offset of the file's possibly-compressed
+// data, relative to the beginning of the zip file.
+//
+// Most callers should instead use Open, which transparently
+// decompresses data and verifies checksums.
+func (f *File) DataOffset() (offset int64, err error) {
+ bodyOffset, err := f.findBodyOffset()
+ if err != nil {
+ return
+ }
+ return f.headerOffset + bodyOffset, nil
+}
+
// Open returns a ReadCloser that provides access to the File's contents.
// Multiple files may be read concurrently.
func (f *File) Open() (rc io.ReadCloser, err error) {
コアとなるコードの解説
追加された DataOffset()
メソッドは、File
型のレシーバを持つ関数です。
-
func (f *File) DataOffset() (offset int64, err error)
:f *File
:File
構造体へのポインタをレシーバとして受け取ります。これにより、File
インスタンスの内部状態(headerOffset
など)にアクセスできます。offset int64
: ZIPアーカイブの先頭からのデータオフセットをint64
型で返します。err error
: 処理中にエラーが発生した場合にエラー情報を返します。
-
bodyOffset, err := f.findBodyOffset()
:- この行は、
File
構造体の内部メソッドであるfindBodyOffset()
を呼び出しています。このメソッドは、ZIPファイルフォーマットの仕様に基づいて、ローカルファイルヘッダの終わりから実際のファイルデータが始まるまでのオフセットを計算します。これには、可変長のファイル名やエクストラフィールドの長さ、そしてオプションのデータ記述子の存在が考慮されます。
- この行は、
-
if err != nil { return }
:findBodyOffset()
の呼び出しでエラーが発生した場合、そのエラーを呼び出し元にそのまま返します。
-
return f.headerOffset + bodyOffset, nil
:f.headerOffset
: これはFile
構造体のフィールドで、このファイルのローカルファイルヘッダがZIPアーカイブの先頭から何バイト目に位置するかを示します。bodyOffset
:findBodyOffset()
によって計算された、ローカルファイルヘッダの開始位置からファイルデータが始まるまでのオフセットです。- これら二つのオフセットを合計することで、ZIPアーカイブの先頭からファイルデータが始まるまでの絶対オフセットが算出されます。エラーがなければ
nil
を返します。
このメソッドは、ZIPファイルフォーマットの低レベルな詳細を抽象化しつつ、必要に応じてファイルデータへの直接アクセスを可能にする、非常に有用なアクセサを提供します。
関連リンク
- Go言語
archive/zip
パッケージのドキュメント: https://pkg.go.dev/archive/zip - Go言語のコードレビューシステム (Gerrit) での変更セット: https://golang.org/cl/12784045
参考にした情報源リンク
- ZIPファイルフォーマットの仕様 (Wikipedia): https://ja.wikipedia.org/wiki/ZIP_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)
- Go言語の
archive/zip
パッケージのソースコード (GitHub): https://github.com/golang/go/tree/master/src/archive/zip - Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の公式ブログや関連ドキュメント (一般的なGoの知識): https://go.dev/blog/
- Go言語の
io
パッケージ (特にio.ReaderAt
について): https://pkg.go.dev/io