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

[インデックス 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 パッケージの EncoderFlush メソッドを追加し、関連するドキュメントを更新しました。また、Flush メソッドの動作を検証するためのテストケースも追加されています。

変更の背景

この変更は、Go issue #6365 を解決するために行われました。このイシューは、encoding/xml パッケージの Encoder が内部的に bufio.Writer を使用しているにもかかわらず、バッファリングされたデータを強制的に書き出す Flush メソッドが提供されていないことに関するものでした。

EncoderEncodeEncodeElement メソッドは内部で 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 を介して実現されます。EncodeEncodeElement のような高レベルのメソッドは、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つのファイルが変更されています。

  1. src/pkg/encoding/xml/marshal.go: Encoder.Flush メソッドの追加と、既存メソッドのドキュメント更新。
  2. 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. という記述が追加されました。これは、EncodeTokenFlush を呼び出さない理由と、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 に書き込む役割を担っています。通常、printerbufio.Writer をラップしているため、この Flush() 呼び出しは bufio.WriterFlush() メソッドに相当し、内部バッファの内容を強制的に書き出します。

この変更に伴い、既存の EncodeEncodeElementEncodeToken メソッドのドキュメントが更新されました。

  • EncodeEncodeElement のドキュメントには、「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 を呼び出さない理由を説明しています。通常、EncodeTokenEncodeEncodeElement のようなより大きな操作の一部として、またはカスタムの 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 を直接、かつ EncodeEncodeElement を介さずに使用する開発者に対する重要な指示です。このようなシナリオでは、XMLデータが実際に基になるライターに書き込まれることを保証するために、明示的に Encoder.Flush() を呼び出す必要があることを強調しています。

これらのドキュメントの更新は、Flush メソッドの導入と合わせて、encoding/xml パッケージのバッファリング動作と、開発者がいつ Flush を呼び出すべきかについての明確なガイドラインを提供します。

関連リンク

参考にした情報源リンク

  • Go issue #6365 (検索結果から直接的なリンクは見つかりませんでしたが、コミットメッセージに記載されているため、このコミットの背景にある問題として参照しています。)
  • Go言語 encoding/xml パッケージ公式ドキュメント: Encoder.Flush の説明 (検索結果から得られた情報に基づいています)