前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.3

17.4. ストリームバッファ

ストリームは、内部バッファ(以下、バッファ)を使用してストレージのデータを読み書きします。

バッファには、サイズが固定長であるものと可変長であるものの 2 種類が存在します。 可変長であるストリームを『可変長バッファストリーム』、 固定であるものを『固定長バッファストリーム』と呼びます。

表 17.6. 固定長バッファストリームと可変長バッファストリーム

項目 固定長バッファストリーム 可変長バッファストリーム
特徴 バッファのサイズは固定 バッファのサイズは読み書きするデータに合わせて可変
取得方法(設定方法) GetStreamReader / GetStreamWriter 関数の size 引数にバッファのサイズを指定する GetStreamReader / GetStreamWriter 関数の size 引数を指定しない
読み書きの方法 バッファのサイズよりも大きなデータは複数回に分けてストリームに読み書きする 1 回のストリームへの読み書き操作でデータ読み書きが完了する
[Tip] Tip

可変長バッファストリームのバッファは、 ストレージから読み込んだり、 書き込んだりするデータに応じて自動的に拡張されます。

メモリを節約したい場合、あるいは、データサイズが予測できない場合は、 固定長バッファストリームを利用して複数回に分けてデータを読み書きします。

[Note] SFXElasticStreamReader / SFXElasticStreamWriter クラス

可変長バッファストリーム経由のデータ読み書きは、 内部的に SFXElasticStreamReader / SFXElasticStreamWriter クラスで行われますが、 開発者はこのクラスについて意識する必要はありません。

17.4.1. 可変長バッファストリーム

可変長バッファストリームとは、 読み書きするデータ合わせてバッファのサイズが自動的に拡張されるストリームのことです。

すべてのデータがバッファに格納されるのでストリームに 1 回の操作で読み書きは完了します。

可変長バッファストリームは、size 引数を指定せずにストレージクラスの GetStreamReader / GetStreamWriter 関数を呼び出すことにより取得できます。

例 17.22. 可変長バッファストリームによるファイル入力

SFXFile file;                     // ファイル
SFXAnsiStringStreamReader reader; // ファイル読み込み用ストリーム
SFXAnsiString string;             // 読み込む文字列

// 読み込みモードでファイルを開く
if ((error = file.OpenReadOnly(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // ファイル読み込み用ストリームを取得する
    // ※ size 引数を指定していないのでストリームバッファは可変長
    if ((error = file.GetStreamReader(&reader)) == SFERR_NO_ERROR) {

        // ファイルからストリームバッファにデータを読み込む
        // ※1. 読み込んだデータのサイズに合わせてストリームバッファは自動的に拡張される
        // ※2. 可変長バッファストリームの場合、1 回の Fetch 関数呼び出しで読み込みは完了する
        if ((error = reader.Fetch()) == SFERR_NO_ERROR) { 

            // ストリームバッファから string 変数にデータを読み込む
            if ((error = reader.ReadSFXAnsiString(&string)) == SFERR_NO_ERROR) { 

                // ファイル読み込み成功!

                // string 変数の内容をデバッグウィンドウに表示する
                TRACE("%s", string.GetCString());
            }
        }
        // ファイル読み込み用ストリームを解放する
        reader.Release();
    }
    // ファイルを閉じる
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // エラーが発生したとき
    ...
}
[Note] 可変長バッファストリームによるファイル読み込み

ストリームにはバッファがあります。

SFXFile::GetStreamReader 関数で size 引数を指定せずにファイル読み込み用ストリームを取得しているので、 可変長バッファが使用されます。 このため、 ストリームバッファは入力ファイルと同じサイズまで拡張されます。

SFXStreamReader::Fetch 関数は、 ファイルからデータを読み込み、ストリームバッファに読み込みます。

SFXAnsiStringStreamReader::ReadSFXAnsiString 関数は、 ストリームバッファからデータを読み込み、string 変数に格納します。

例 17.23. 可変長バッファストリームによるファイル出力

SFXFile file;                     // ファイル
SFXAnsiStringStreamWriter writer; // ファイル書き込み用ストリーム
SFXAnsiString string("abcdefg");  // 書き込む文字列

// 読み書きモードでファイルを開く
if ((error = file.OpenReadWrite(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // ファイル書き込み用ストリームを取得する
    // ※ size 引数を指定していないのでストリームバッファは可変長
    if ((error = file.GetStreamWriter(&writer)) == SFERR_NO_ERROR) {

        // string 変数からストリームバッファにデータを書き込む
        // ※ 書き込んだデータのサイズに合わせてストリームバッファは自動的に拡張される
        if ((error = writer.WriteSFXAnsiString(string)) == SFERR_NO_ERROR) {

            // ストリームバッファからファイルにデータを書き込む
            // ※ 可変長バッファストリームの場合、1 回の Flush 関数呼び出しで書き込みは完了する
            error = writer.Flush(); 
        }
        // ファイル書き込み用ストリームを解放する
        writer.Release();
    }
    // ファイルを閉じる
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // エラーが発生したとき
    ...
}
[Note] 可変長バッファストリームによるファイル書き込み

ストリームにはバッファがあります。

SFXFile::GetStreamWriter 関数で size 引数を指定せずにファイル書き込み用ストリームを取得しているので、 可変長バッファが使用されます。 このため、 ストリームバッファは出力ファイルに書き込む全データと同じサイズまで拡張されます。

SFXAnsiStringStreamWriter::WriteSFXAnsiString 関数は、 string 変数のデータをストリームバッファに書き込みます。

SFXStreamWriter::Flush 関数は、 ストリームバッファ内のデータをファイルに書き込みます。

例 17.24. 可変長バッファストリームによる TCP ソケット通信

// コールバック関数で使用するので、_socket はクラスのメンバ変数として定義する
class MyClass {
private:
    SFXTCPSocket _socket;                // SFXTCPSocket インスタンス
    SFXAnsiStringStreamReader  _reader;  // データ受信用ストリーム
    SFXAnsiStringStreamWriter  _writer;  // データ送信用ストリーム
public:
    Void Start(Void);
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
};

Void MyClass::Start(Void)
{
    SFCError error;
    SFXSocketAddress host("www.example.com:80");

    // ソケットを開く
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // サーバーに接続する
        // ※1. 接続要求の結果は、OnConnect 関数に通知される
        // ※2. ホスト名(host)は自動的に解決される
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// 接続要求の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    // 送信する文字列
    static AChar message[] = "GET / HTTP/1.0\r\n\r\n";

    if (error == SFERR_NO_ERROR) {

        // データ送信用ストリームを取得する
        // ※ size 引数を指定していないのでストリームバッファは可変長
        if ((error = _socket.GetStreamWriter(&_writer)) == SFERR_NO_ERROR) {

            // message 変数からストリームバッファにデータを書き込む
            // ※ message 変数のサイズに合わせてバッファは自動的に拡張される
            if ((error = _writer.Write(message, lengthof(message))) == SFERR_NO_ERROR) {

                // フラッシュを行う: 実際にストリームバッファからソケットにデータを送信する
                // ※ データ送信(フラッシュ)の結果は、OnFlush 関数に通知される
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

       // エラーが発生したとき
       // データ送信用ストリームを解放する
       _writer.Release();
       // ソケットを閉じる
       _socket.Close();
    }
    return;
}

// データ送信(フラッシュ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    // 送信が終わったのでデータ送信用ストリームを解放する
    _writer.Release();

    if (error == SFERR_NO_ERROR) {

        // データ受信用ストリームを取得する
        // ※ size 引数を指定していないのでストリームバッファは可変長
        if ((error = _socket.GetStreamReader(&_reader)) == SFERR_NO_ERROR) {

            // フェッチを行う: 実際にソケットからストリームバッファにデータを受信する
            // ※1. データ受信(フェッチ)の結果は、OnFetch 関数に通知される
            // ※2. 受信するデータのサイズに合わせてバッファは自動的に拡張される
            error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // データ受信用ストリームを解放する
        _reader.Release(); 
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ受信(フェッチ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // ストリームバッファから string 変数にデータを読み込む
        _reader >> string;

        // string 変数の内容をデバッグウィンドウに表示する
        TRACE("%s", string.GetCString());
    }

    // 受信が終わったのでデータ受信用ストリームを解放する
    _reader.Release();
    // ソケットを閉じる
    _socket.Close();
    return;
}
[Note] 可変長バッファストリームによる TCP ソケット通信

ストリームにはバッファがあります。

SFXTCPSocket::GetStreamReader / SFXTCPSocket::GetStreamWriter 関数で size 引数を指定せずにデータ送受信用ストリームを取得しているので、 可変長バッファが使用されます。 このため、ストリームバッファに送受信する全データと同じサイズまで拡張されます。

SFXAnsiStringStreamWriter::WriteSFXAnsiString 関数は、 string 変数のデータをストリームバッファに書き込みます。

SFXAnsiStringStreamReader::ReadSFXAnsiString 関数は、 ストリームバッファからデータを読み込み、string 変数に格納します。

SFXStreamWriter::Flush 関数は、 ストリームバッファ内のデータを TCP ソケット通信ネットワークに送信します。

SFXStreamReader::Fetch 関数は、 TCP ソケット通信ネットワークからデータを受信し、ストリームバッファに読み込みます。

17.4.2. 固定長バッファストリーム

固定長バッファストリームとは、 読み書きするデータに関係なくバッファのサイズが固定であるストリームのことです。

データがバッファサイズよりも大きい場合はストリームに複数回読み書きを行う必要があります。

固定長バッファストリームは、size 引数を指定してストレージの GetStreamReader / GetStreamWriter 関数を呼び出すことにより取得できます。 バッファサイズは、size 引数に指定した値で設定されます。

例 17.25. 固定長バッファストリームによるファイル入力

SFCError error;  // エラー値
SFXFile file;    // ファイルクラス のインスタンス
SFXAnsiStringStreamReader reader; // ファイル読み込み用ストリーム
SFXAnsiString stringFromFile;     // 読み込む文字列
SFXAnsiString tempString;

// 読み込みモードでファイルを開く
if ((error = file.OpenReadOnly(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // ファイル読み込み用ストリームを取得する
    // ※ size 引数を指定しているのでストリームバッファは固定長(バッファサイズ: 1024 バイト)
    if ((error = file.GetStreamReader(1024, &reader)) == SFERR_NO_ERROR) {

        // ファイルの終端に到達するまで繰り返す
        while ((error == SFERR_NO_ERROR) && !reader.Ends()) {

            // ※ バッファサイズよりも大きなデータは複数回 Fetch 関数を呼び出して読み込む

            // フェッチを行う: ファイルからストリームバッファにデータを読み込む
            // ※ ストリームバッファは固定長(バッファサイズ: 1024 バイト)なので、1024 バイトまでのデータしか読み込まれない
            if ((error = reader.Fetch()) == SFERR_NO_ERROR) {

                // tempString にデータを読み込む
                if ((error = reader.ReadSFXAnsiString(&tempString)) == SFERR_NO_ERROR) { 

                    // stringFromFile の末尾に tempString を加える
                    stringFromFile += tempString;
                }
            }
        }
        // ファイル読み込み用ストリームを解放する
        reader.Release();
    }
    // ファイルを閉じる
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // エラーが発生したとき
    ...
}
[Note] 固定長バッファストリームによるファイル読み込み

SFXFile::GetStreamReader 関数の呼び出しで size 引数にバッファサイズ 1024 バイトを設定しているので、 このサイズの固定長ストリームバッファを使用するストリーム経由でファイル読み込みが行われます。

1 回の SFXStreamReader::Fetch 関数の呼び出しで 1024 バイトまでのデータしか読み込まれません。

1024 バイトよりも大きなデータの読み込みは、 SFXStreamReader::Fetch 関数と SFXAnsiStringStreamReader::ReadSFXAnsiString 関数の呼び出しを複数回繰り返す必要があります。

例 17.26. 固定長バッファストリームによるファイル出力

SFCError error;  // エラー値
SFXFile file;    // ファイル
SFXAnsiStringStreamWriter writer;                      // ファイル書き込み用ストリーム
SFXAnsiString string("abcdefghijklmnopqrstuvwxyz");    // 書き込む文字列
ACharConstPtr _p = string.GetBuffer();                 // 文字列を指すポインタ
ACharConstPtr _endOfString = _p + string.GetLength();  // 文字列の末尾

SInt32 bufferSize = 1024;

// 読み書きモードでファイルを開く
if ((error = file.OpenReadWrite(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // ファイル書き込み用ストリームを取得する
    // ※ size 引数を指定しているのでストリームバッファは固定長(バッファサイズ: 1024 バイト)
    if ((error = file.GetStreamWriter(bufferSize, &writer)) == SFERR_NO_ERROR) {

        for (; (error == SFERR_NO_ERROR) && (_p < _endOfString) ; _p += bufferSize) { 

            // バッファサイズよりも大きなデータは複数回 Flush 関数を呼び出して書き込む

            // 書き込む文字列のサイズ
            SInt32 size = (_endOfString - _p < bufferSize) ? _endOfString - _p : bufferSize;

            // _p が指す長さ size の文字列をストリームバッファに書き込む
            if ((error = writer.Write(_p, size)) == SFERR_NO_ERROR) {

                // ストリームバッファからファイルにデータを書き込む
                error = writer.Flush();
            }
        }
        // ファイル書き込み用ストリームを解放する
        writer.Release();
    }
    // ファイルを閉じる
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // エラーが発生したとき
    ...
}
[Note] 固定長バッファストリームによるファイル書き込み

SFXFile::GetStreamWriter 関数の呼び出しで size 引数にバッファサイズ 1024 バイトを設定しているので、 このサイズの固定長ストリームバッファを使用するストリーム経由でファイル書き込みが行われます。

1 回の SFXStreamWriter::Write 関数の呼び出しで 1024 バイトまでのデータしかストリームバッファに書き込めません。 そのため、1 回の SFXStreamWriter::Flush 関数の呼び出しで 1024 バイトまでのデータしかファイルに書き込まれません。

1024 バイトよりも大きなデータの書き込みは、 SFXStreamWriter::Write 関数と SFXStreamWriter::Flush 関数の呼び出しを複数回繰り返す必要があります。

例 17.27. 固定長バッファストリームによる TCP ソケット通信

// コールバック関数で使用するので、_socket はクラスのメンバ変数として定義する
class MyClass {
private:
    SFXTCPSocket _socket;                // TCP ソケットクラス
    SFXAnsiStringStreamReader  _reader;  // データ受信用ストリーム
    SFXAnsiStringStreamWriter  _writer;  // データ送信用ストリーム
    SFXAnsiString _sendString;           // 送信する文字列
    ACharConstPtr _p;
    ACharConstPtr _end;
    SInt32 _bufferSize;
    SFXAnsiString _receiveString;        // 受信した文字列
public:
    Void Start(Void);
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
};

Void MyClass::Start(Void)
{
    SFCError error;
    SFXSocketAddress host("www.example.com:80");

    // 送信する文字列
    _sendString = "GET / HTTP/1.0\r\n\r\n";
    // バッファサイズ: 1024
    _bufferSize = 1024;

    // ソケットを開く
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // サーバーに接続する
        // ※1. 接続要求の結果は、OnConnect 関数に通知される
        // ※2. ホスト名(host)は自動的に解決される
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// 接続要求の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        // データ送信用ストリームを取得する
        // ※ size 引数を指定しているのでストリームバッファは固定長(1024 バイト)
        if ((error = _socket.GetStreamWriter(_bufferSize, &_writer)) == SFERR_NO_ERROR) {


            _p = _sendString.GetBuffer();
            _end = _p + _sendString.GetLength();

            SInt32 size = (_end - _p < _bufferSize) ? _end - _p : _bufferSize;

            // _p が指す長さ size の文字列をストリームバッファに書き込む
            if ((error = _writer.Write(_p, size) == SFERR_NO_ERROR) {

                _p += size;

                // フラッシュを行う: 実際にストリームバッファからソケットにデータを送信する
                // ※ データ送信(フラッシュ)の結果は、OnFlush 関数に通知される
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // データ送信用ストリームを解放する
        _writer.Release();
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ送信(フラッシュ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    if (error == SFERR_NO_ERROR) {

        if (_p < _end) { 
            // 送信するデータがまだ存在するとき: 

            // バッファサイズよりも大きなデータは複数回 Flush 関数を呼び出して送信する

            SInt32 size = (_end - _p < _bufferSize) ? _end - _p : _bufferSize;

            // _p が指す長さ size の文字列をストリームバッファに書き込む
            if ((error = _writer.Write(_p, size) == SFERR_NO_ERROR) {

                _p += size;

                // フラッシュを行う: 実際にストリームバッファからソケットにデータを送信する
                // ※ データ送信(フラッシュ)の結果は、OnFlush 関数に通知される
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
            if (error != SFERR_NO_ERROR) { 

                // エラーが発生したとき
                // データ送信用ストリームを解放する
                _writer.Release();
            }

        }
        else {

            // すべてのデータを送信し終えたとき: 

            // データ送信用ストリームを解放する
            _writer.Release();

            // 受信を開始する

            // データ受信用ストリームを取得する
            // ※ size 引数を指定しているのでストリームバッファは固定長(1024 バイト)
            if ((error = _socket.GetStreamReader(_bufferSize, &_reader)) == SFERR_NO_ERROR) {

                // フェッチを行う: 実際にソケットからストリームバッファにデータを受信する
                // ※ データ受信(フェッチ)の結果は、OnFetch 関数に通知される
                error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // データ受信用ストリームを解放する
        _reader.Release(); 
        // データ送信用ストリームを解放する
        _writer.Release();
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}

// データ受信(フェッチ)の結果が通知されるコールバック関数
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // ストリームバッファから string 変数にデータを読み込む
        if ((error = _reader.ReadSFXAnsiString(&string)) != SFERR_NO_ERROR) { 

            _receiveString += string;

            if (!_reader.Ends()) {

                // 受信するデータがまだ残っている場合:

                // バッファサイズよりも大きなデータは複数回 Fetch 関数を呼び出して受信する

                // フェッチを行う: 実際にソケットからストリームバッファにデータを受信する
                // ※ データ受信(フェッチ)の結果は、OnFetch 関数に通知される
                error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
            } else {

                // すべてのデータを受信したとき:

                // 受信したデータをデバッグウィンドウに表示する
                TRACE("--------");
                TRACE("%s", _receiveString.GetCString());
                TRACE("--------");

                // データ受信用ストリームを解放する
                _reader.Release();
                // ソケットを閉じる
                _socket.Close();
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // エラーが発生したとき
        // ストリームを解放する
        _reader.Release(); 
        // ソケットを閉じる
        _socket.Close();
    }
    return;
}
[Note] 固定長バッファストリーム経由のデータ送受信

バッファサイズよりも大きなデータの受信は、 SFXStreamReader::Fetch 関数と、 SFXAnsiStringStreamReader::ReadSFXAnsiString 関数を複数回呼び出して行います。 SFXStreamReader::Fetch 関数を呼び出すたびに OnFetch 関数が起動されます。

一方、バッファサイズよりも大きなデータの送信は、 SFXStreamWriter::Write 関数と SFXStreamWriter::Flush 関数を複数回呼び出して行います。 SFXStreamWriter::Flush 関数を呼び出すたびに OnFlush 関数が起動されます。