[インデックス 17587] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/xml パッケージに Encoder.Flush メソッドを追加するものです。これにより、XMLエンコーダのバッファリングされたデータを強制的に基になるライターに書き出す機能が提供され、特に EncodeToken を直接使用する場合のデータの一貫性と即時性が向上します。
コミット
commit 3c11dd8ebc46a306c7fb196da63328426160cd6a
Author: Russ Cox <rsc@golang.org>
Date: Thu Sep 12 16:54:01 2013 -0400
encoding/xml: add Encoder.Flush
Fixes #6365.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13627046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3c11dd8ebc46a306c7fb196da63328426160cd6a
元コミット内容
encoding/xml パッケージの Encoder に Flush メソッドを追加し、関連するドキュメントを更新しました。また、Flush メソッドの動作を検証するためのテストケースも追加されています。
変更の背景
この変更は、Go issue #6365 を解決するために行われました。このイシューは、encoding/xml パッケージの Encoder が内部的に bufio.Writer を使用しているにもかかわらず、バッファリングされたデータを強制的に書き出す Flush メソッドが提供されていないことに関するものでした。
Encoder の Encode や EncodeElement メソッドは内部で Flush を呼び出すため、通常の使用では問題になりません。しかし、EncodeToken メソッドを直接使用してXMLトークンを書き込む場合、EncodeToken 自体は Flush を呼び出しません。このため、ユーザーが EncodeToken を繰り返し呼び出してXMLを構築する際に、データが内部バッファに留まり、基になる io.Writer に書き込まれないという問題が発生していました。特に、ネットワークストリームなど、データが即座に送信されることが期待されるシナリオでは、このバッファリングが問題となる可能性がありました。
Flush メソッドの追加により、開発者は必要に応じて明示的にバッファをフラッシュし、XMLデータが確実に基になるライターに書き込まれるようにすることができるようになりました。
前提知識の解説
encoding/xmlパッケージ: Go言語の標準ライブラリの一部で、Goの構造体とXMLの間でデータをエンコード(マーシャリング)およびデコード(アンマーシャリング)するための機能を提供します。Encoder:encoding/xmlパッケージの中心的な型の一つで、Goの値をXML形式でio.Writerに書き込むための機能を提供します。内部でバッファリングを行うことで、I/O操作の効率を高めています。Token: XMLドキュメントの構成要素(例: 開始タグ、終了タグ、文字データ、コメントなど)を表すインターフェースです。Encoder.EncodeTokenメソッドは、これらのトークンを一つずつ書き込むために使用されます。bufio.Writer:bufioパッケージで提供されるバッファリングされたライターです。I/O操作の回数を減らすことで、パフォーマンスを向上させます。データはまず内部バッファに書き込まれ、バッファが満杯になるか、明示的にフラッシュされるか、またはライターがクローズされるまで、基になるio.Writerには書き込まれません。- フラッシュ (Flush): バッファリングされたI/Oにおいて、内部バッファに蓄積されたデータを強制的に基になるI/Oデバイス(この場合は
io.Writer)に書き出す操作を指します。これにより、データが確実に永続化されるか、または次の処理段階に渡されることが保証されます。
技術的詳細
encoding/xml パッケージの Encoder は、効率的なXML生成のために内部的にバッファリングメカニズムを使用しています。これは通常、bufio.Writer を介して実現されます。Encode や EncodeElement のような高レベルのメソッドは、XMLのエンコードが完了した後に自動的に内部バッファをフラッシュするように設計されています。これにより、これらのメソッドを使用する開発者は、データの書き込みについて意識する必要がありませんでした。
しかし、EncodeToken メソッドは、より低レベルなXMLトークンのストリーム処理を目的としており、単一のトークンを書き込むたびにフラッシュを行うとパフォーマンスが低下する可能性があるため、自動的なフラッシュは行われませんでした。これは、カスタムの Marshaler 実装や、XMLをトークン単位で細かく制御して生成するような高度なシナリオで EncodeToken が使用されることを想定しているためです。
この設計上の選択は、EncodeToken を直接使用する際に、データがバッファに留まり、期待通りに基になるライターに書き込まれないという問題を引き起こしました。特に、XMLデータをリアルタイムで処理する必要がある場合や、部分的なXMLドキュメントをストリームとして送信する場合に、この遅延が問題となります。
Encoder.Flush() メソッドの追加は、この問題を解決するための直接的なアプローチです。このメソッドは、Encoder の内部 printer 型が持つ Flush メソッドを呼び出すことで、bufio.Writer のバッファを強制的にフラッシュします。これにより、EncodeToken を使用してXMLを構築する開発者は、必要に応じて明示的に Flush() を呼び出すことで、バッファリングされたXMLデータが即座に基になる io.Writer に書き込まれることを保証できるようになりました。
この変更は、encoding/xml パッケージの柔軟性を高め、より多様なXML生成シナリオに対応できるようにするものです。
コアとなるコードの変更箇所
このコミットでは、主に以下の2つのファイルが変更されています。
src/pkg/encoding/xml/marshal.go:Encoder.Flushメソッドの追加と、既存メソッドのドキュメント更新。src/pkg/encoding/xml/marshal_test.go:Encoder.Flushメソッドの動作を検証するテストケースの追加。
src/pkg/encoding/xml/marshal.go の変更点
-
Encoder.Flush()メソッドの追加:// Flush flushes any buffered XML to the underlying writer. // See the EncodeToken documentation for details about when it is necessary. func (enc *Encoder) Flush() error { return enc.p.Flush() }この新しいメソッドは、
Encoderの内部printer型のFlushメソッドを呼び出すことで、バッファリングされたXMLデータを基になるライターに書き出します。 -
Encoder.Encode()メソッドのドキュメント更新:Encode calls Flush before returning.という記述が追加され、Encodeメソッドが戻る前にFlushを呼び出すことが明記されました。 -
Encoder.EncodeElement()メソッドのドキュメント更新:EncodeElement calls Flush before returning.という記述が追加され、EncodeElementメソッドが戻る前にFlushを呼び出すことが明記されました。 -
Encoder.EncodeToken()メソッドのドキュメント更新:EncodeToken does not call Flush, because usually it is part of a larger operation such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked during those), and those will call Flush when finished.およびCallers that create an Encoder and then invoke EncodeToken directly, without using Encode or EncodeElement, need to call Flush when finished to ensure that the XML is written to the underlying writer.という記述が追加されました。これは、EncodeTokenがFlushを呼び出さない理由と、EncodeTokenを直接使用する場合にFlushを呼び出す必要があることを明確にしています。
src/pkg/encoding/xml/marshal_test.go の変更点
TestMarshalFlush()テスト関数の追加:
このテストは、func TestMarshalFlush(t *testing.T) { var buf bytes.Buffer enc := NewEncoder(&buf) if err := enc.EncodeToken(CharData("hello world")); err != nil { t.Fatalf("enc.EncodeToken: %v", err) } if buf.Len() > 0 { t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) } if err := enc.Flush(); err != nil { t.Fatalf("enc.Flush: %v", err) } if buf.String() != "hello world" { t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") } }EncodeTokenでデータを書き込んだ後、Flushを呼び出すまでbytes.Bufferにデータが書き込まれないことを確認し、Flush呼び出し後にデータが正しく書き込まれることを検証しています。
コアとなるコードの解説
このコミットの核心は、encoding/xml.Encoder 型に Flush() メソッドを追加したことです。
// Flush flushes any buffered XML to the underlying writer.
// See the EncodeToken documentation for details about when it is necessary.
func (enc *Encoder) Flush() error {
return enc.p.Flush()
}
このメソッドは、Encoder の内部フィールドである p (型は printer) の Flush() メソッドを呼び出しています。printer 型は、XMLデータを実際にバッファリングし、基になる io.Writer に書き込む役割を担っています。通常、printer は bufio.Writer をラップしているため、この Flush() 呼び出しは bufio.Writer の Flush() メソッドに相当し、内部バッファの内容を強制的に書き出します。
この変更に伴い、既存の Encode、EncodeElement、EncodeToken メソッドのドキュメントが更新されました。
-
EncodeとEncodeElementのドキュメントには、「Encode calls Flush before returning.」および「EncodeElement calls Flush before returning.」という記述が追加されました。これは、これらの高レベルなエンコードメソッドが、処理の完了時に自動的にバッファをフラッシュすることを明確にしています。これにより、これらのメソッドを使用する開発者は、通常、明示的にFlush()を呼び出す必要がないことが示唆されます。 -
EncodeTokenのドキュメントは、より詳細な説明が追加されました。- 「
EncodeToken does not call Flush, because usually it is part of a larger operation such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked during those), and those will call Flush when finished.」 これは、EncodeTokenが単独でFlushを呼び出さない理由を説明しています。通常、EncodeTokenはEncodeやEncodeElementのようなより大きな操作の一部として、またはカスタムのMarshalerの中で使用されるため、それらの上位の操作が最終的にFlushを担当するという設計思想が示されています。 - 「
Callers that create an Encoder and then invoke EncodeToken directly, without using Encode or EncodeElement, need to call Flush when finished to ensure that the XML is written to the underlying writer.」 この文は、EncodeTokenを直接、かつEncodeやEncodeElementを介さずに使用する開発者に対する重要な指示です。このようなシナリオでは、XMLデータが実際に基になるライターに書き込まれることを保証するために、明示的にEncoder.Flush()を呼び出す必要があることを強調しています。
- 「
これらのドキュメントの更新は、Flush メソッドの導入と合わせて、encoding/xml パッケージのバッファリング動作と、開発者がいつ Flush を呼び出すべきかについての明確なガイドラインを提供します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/3c11dd8ebc46a306c7fb196da63328426160cd6a
- Go CL (Change List): https://golang.org/cl/13627046
参考にした情報源リンク
- Go issue #6365 (検索結果から直接的なリンクは見つかりませんでしたが、コミットメッセージに記載されているため、このコミットの背景にある問題として参照しています。)
- Go言語
encoding/xmlパッケージ公式ドキュメント:Encoder.Flushの説明 (検索結果から得られた情報に基づいています)