PrevNextUpHome SophiaFramework UNIVERSE 5.3

17.5. Stream Buffer

The stream reads / writes data from / into the storage by using the internal buffer(hereafter, "buffer").

There are two types of buffers from the point of the view of the size, i.e., one is fixed and another is variable. The stream with the variable buffer is called as "variable buffer stream", and one with the fixed buffer is called as "fixed buffer stream".

Table 17.6. Fixed Buffer Stream and Variable Buffer Stream

Item Fixed Buffer Stream Variable Buffer Stream
Feature The buffer size is fixed. The buffer size is variable depending on the size of data to read or write.
Method to Get (or Set) Specify the buffer size in the size argument of the GetStreamReader / GetStreamWriter function. Don't specify the size argument of the GetStreamReader / GetStreamWriter function.
Method to Read or Write For data bigger than the buffer size, operation completes with reading from / writing into the stream in multiple times. Operation always completes with reading from / writing into the stream once.
[Tip] Tip

The buffer of the variable buffer stream is automatically expanded depending on data read from / written into the storage.

When it is necessary to save memory or the data size cannot be estimated, it is recommended to read / write data in multiple times with the fixed buffer stream.

[Note] SFXElasticStreamReader / SFXElasticStreamWriter Class

Though the reading / writing through the variable buffer stream is internally performed with the SFXElasticStreamReader / SFXElasticStreamWriter class, you don't have to be aware of this class.

17.5.1. Variable Buffer Stream

The variable buffer stream is the stream whose buffer will be automatically expanded depending on the size of data to be read from / written into the storage.

Since all data is stored into the buffer, operation will complete with reading from / writing into the stream one time.

The variable buffer stream can be gotten by calling the GetStreamReader / GetStreamWriter function of the storage class without specifying the size argument to set the buffer size.

Example 17.22. Reading Data from File with Variable Buffer Stream

SFXFile file;                     // file
SFXAnsiStringStreamReader reader; // stream for reading data from the file
SFXAnsiString string;             // data to read from the file

// open the file in the read only mode
if ((error = file.OpenReadOnly(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // get the stream for reading data from the file
    // * the stream buffer is variable since the size argument is not specified
    if ((error = file.GetStreamReader(&reader)) == SFERR_NO_ERROR) {

        // perform fetch: read data from the file into the stream buffer
        // *1. the stream buffer will be expanded automatically depending on the size of data to be read into
        // *2. the read operation will finish by calling the Fetch function once
        if ((error = reader.Fetch()) == SFERR_NO_ERROR) { 

            // read data from the stream buffer into the string variable
            if ((error = reader.ReadSFXAnsiString(&string)) == SFERR_NO_ERROR) { 

                // succeed to read data from the file!

                // display the content of the string variable on BREW Output Window
                TRACE("%s", string.GetCString());
            } 
        }
        // release the stream for reading from the file
        reader.Release();
    }
    // close the file
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // if an error occurs
    ...
}
[Note] Reading Data from File with Variable Buffer Stream

Each stream has its own buffer(stream buffer).

Since the stream for reading data from the file is gotten by calling the SFXFile::GetStreamReader function without the size argument, the variable buffer will be used. Therefore, the stream buffer will be expanded to the same size with the input file.

The SFXStreamReader::Fetch function reads data from the file into the stream buffer.

The SFXAnsiStringStreamReader::ReadSFXAnsiString function reads data from the stream buffer into the specified variable.

Example 17.23. Writing Data into File with Variable Buffer Stream

SFXFile file;                     // file
SFXAnsiStringStreamWriter writer; // stream for writing data into the file
SFXAnsiString string("abcdefg");  // data to write into the file

// open the file in the read-write mode
if ((error = file.OpenReadWrite(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // get the stream for writing data into the file
    // * the stream buffer is variable since the size argument is not specified
    if ((error = file.GetStreamWriter(&writer)) == SFERR_NO_ERROR) {

        // write data from the string variable into the stream buffer
        // * the stream buffer will be expanded automatically depending on the size of data to be written
        if ((error = writer.WriteSFXAnsiString(string)) == SFERR_NO_ERROR) {

            // perform flush: write data from the stream buffer into the file
            // * the write operation will finish by calling the Flush function once
            error = writer.Flush();
        } 
        // release the stream for writing into the file
        writer.Release();
    } 
    // close the file
    file.Close();
} 
if (error != SFERR_NO_ERROR) {

    // if an error occurs
    ...
}
[Note] Writing Data into File with Variable Buffer Stream

Each stream has its own buffer(stream buffer).

Since the stream for writing data into the file is gotten by calling the SFXFile::GetStreamWriter function without the size argument, the variable buffer will be used. Therefore, the stream buffer will be expanded to the same size with the output file.

The SFXAnsiStringStreamWriter::WriteSFXAnsiString function writes data from the string variable into the stream buffer.

The SFXStreamWriter::Flush function writes data from the stream buffer into the file.

Example 17.24. TCP Socket Communication with Variable Buffer Stream

// The _socket variable is defined as class member variable since used in the callback function
class MyClass {
private:
    SFXTCPSocket _socket;                // SFXTCPSocket instance
    SFXAnsiStringStreamReader  _reader;  // input stream
    SFXAnsiStringStreamWriter  _writer;  // output stream
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");

    // open the TCP socket
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // connect to the TCP server
        // *1. the connection result will be notified to the OnConnect function
        // *2. the host name will be automatically resolved
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the connection result
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    // string to send
    static AChar message[] = "GET / HTTP/1.0\r\n\r\n";

    if (error == SFERR_NO_ERROR) {

        // get the variable buffer stream for sending data
        // * the stream buffer is variable since the size argument is not specified
        if ((error = _socket.GetStreamWriter(&_writer)) == SFERR_NO_ERROR) {

            // write data from the message variable into the stream buffer
            // * the stream buffer will be expanded automatically depending on the size of data to be written
            if ((error = _writer.Write(message, lengthof(message))) == SFERR_NO_ERROR) {

                // perform flush: send data from the stream buffer to the socket actually
                // * the flush result will be notified to the OnFlush function
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // release the _writer stream
        _writer.Release();
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the flush result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    // release the _writer stream since sending data has been done
    _writer.Release();

    if (error == SFERR_NO_ERROR) {

        // get the variable buffer stream for receiving data
        // * the stream buffer is variable since the size argument is not specified
        if ((error = _socket.GetStreamReader(&_reader)) == SFERR_NO_ERROR) {

            // perform fetch: receive data from the socket into the stream buffer actually
            // *1. the fetch result will be notified to the OnFetch function
            // *2. the stream buffer will be expanded automatically depending on the size of data to be received
            error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // release the _reader stream
        _reader.Release(); 
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the fetch result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // read data from the stream buffer into the string variable
        _reader >> string;

        // display the content of the string variable on BREW Output Window
        TRACE("%s", string.GetCString());
    }

    // release the _reader stream since receiving data has been done
    _reader.Release();
    // close the socket
    _socket.Close();
    return;
}
[Note] TCP Socket Communication with Variable Buffer Stream

Each stream has its own buffer(stream buffer).

Since the stream for receiving from / sending to the TCP socket is gotten by calling the SFXTCPSocket::GetStreamReader / SFXTCPSocket::GetStreamWriter function with no size argument, the variable buffer will be used. Therefore, the stream buffer will be expanded to the same size with data to receive / send.

The SFXAnsiStringStreamWriter::WriteSFXAnsiString function writes data from the string variable into the stream buffer.

The SFXAnsiStringStreamReader::ReadSFXAnsiString function reads data from the stream buffer into the string variable.

The SFXStreamWriter::Flush function sends data from the stream buffer into the TCP socket.

The SFXStreamReader::Fetch function receives data from the TCP socket into the stream buffer.

17.5.2. Fixed Buffer Stream

The fixed buffer stream is the stream whose buffer size is fixed regardless of the size of data to read from / write into the storage.

If data to read / write is bigger than the buffer size, you will have to read from / write into the stream in multiple times.

The fixed buffer stream can be obtained by calling the GetStreamReader / GetStreamWriter function of the storage class with specifying the size argument to set the buffer size.

Example 17.25. Reading Data from File with Fixed Buffer Stream

SFCError error;                   // error value
SFXFile file;                     // input file
SFXAnsiStringStreamReader reader; // input stream
SFXAnsiString stringFromFile;     // for storing data
SFXAnsiString tempString;

// open the file in the read-only mode
if ((error = file.OpenReadOnly(SFXPath("/dir1/data.txt"))) == SFERR_NO_ERROR) {

    // get the input stream for reading data from the file
    // * since the size argument is specified,  the stream buffer size is fixed (buffer size: 1024)
    if ((error = file.GetStreamReader(1024, &reader)) == SFERR_NO_ERROR) {

        while ((error == SFERR_NO_ERROR) && !reader.Ends()) {

            // * repeat the following processing until the end of the file is reached
            // data greater than the stream buffer size are read by calling the Fetch function in multiple times

            // perform fetch: read data from the input file into the stream buffer actually
            // * since the stream buffer size is fixed by 1024 byte, data more than 1024 byte cannot be read per the fetch operation
            if ((error = reader.Fetch()) == SFERR_NO_ERROR) {

                // read data from the stream buffer into the tempString variable
                if ((error = reader.ReadSFXAnsiString(&tempString)) == SFERR_NO_ERROR) { 

                    // succeed to read!

                    // append tempString to the end of the stringFromFile variable
                    stringFromFile += tempString;

                }
            }
        }
        // release the input stream for reading data from the file
        reader.Release();
    }
    // close the file
    file.Close();
}
if (error != SFERR_NO_ERROR) {

    // if an error occurs
    ...
}

In the above example, since the buffer size is set to 1024 byte, more than 1024 byte cannot be read per the fetch operation. To read data more than 1024 byte, repeat the fetch operation several times.

Example 17.26. Writing Data into File with Fixed Buffer Stream

SFCError error;                                      // error value
SFXFile file;                                        // output file
SFXAnsiStringStreamWriter writer;                    // output stream
SFXAnsiString string("abcdefghijklmnopqrstuvwxyz");  // data to write
ACharConstPtr _p = string.GetBuffer();                // pointer to string
ACharConstPtr _endOfString = _p + string.GetLength();  // end of string

SInt32 bufferSize = 1024;

// open the file in the read write mode
if ((error = file.OpenReadWrite(SFXPath("/dir/data.txt"))) == SFERR_NO_ERROR) {

    // get the output stream for writing data into the file
    // * since the size argument is specified,  the stream buffer size is fixed (buffer size: 1024)
    if ((error = file.GetStreamWriter(bufferSize, &writer)) == SFERR_NO_ERROR) {

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

            // data greater than the stream buffer size are written by calling the Flush function in multiple times

            // size of the string to be written into the file
            SInt32 size = (_endOfString - _p < bufferSize) ? _endOfString - _p : bufferSize;

            // write the _p string into the stream buffer
            if ((error = writer.Write(_p, size)) == SFERR_NO_ERROR) {

                // perform flush: write data from the stream buffer into the file actually
                error = writer.Flush(); 
            } 
        }
        // release the output stream for writing data into the file
        writer.Release();
    }
    // close the file
    file.Close();
} 
if (error != SFERR_NO_ERROR) {

    // if an error occurs
    ...
}
[Note] Writing Data into File with Fixed Buffer Stream

Since the buffer size of 1024 bytes is specified in the size argument when the SFXFile::GetStreamWriter function is called, data will be written into the file using the fixed buffer stream.

The maximum size of data that can be written into the stream buffer per calling the SFXStreamWriter::Write function is 1024 bytes. Therefore, The maximum size of data that can be written into the file per calling the SFXStreamWriter::Flush function is also 1024 bytes.

To write data greater than 1024 bytes into the file, it is necessary to call the SFXStreamWriter::Write function and the SFXStreamWriter::Flush function in multiple times.

Example 17.27. TCP Socket Communication with Fixed Buffer Stream

// The _socket variable is defined as class member variable since used in the callback function
class MyClass {
private:
    SFXTCPSocket _socket;                // TCP Socket instance
    SFXAnsiStringStreamReader  _reader;  // input stream
    SFXAnsiStringStreamWriter  _writer;  // output stream
    SFXAnsiString _sendString;           // string to send
    ACharConstPtr _p;
    ACharConstPtr _end;
    SInt32 _bufferSize;
    SFXAnsiString _receiveString;        // string to be received
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");

    // string to send
    _sendString = "GET / HTTP/1.0\r\n\r\n";
    // buffer size: 1024
    _bufferSize = 1024;

    // open the socket
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // connect to the TCP server
        // *1. the connection result will be notified to the OnConnect function
        // *2. the host name will be automatically resolved
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the connection result
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        // get the input stream for receiving data from the socket
        // * since the size argument is specified,  the stream buffer size is fixed (buffer 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;

            // write the _p string into the stream buffer
            if ((error = _writer.Write(_p, size) == SFERR_NO_ERROR) {

                _p += size;

                // perform flush: send data from the stream buffer to the socket actually
                // * the flush result will be notified to the OnFlush function
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // release the output stream for sending data to the socket
        _writer.Release();
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the flush result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    if (error == SFERR_NO_ERROR) {

        if (_p < _end) { 

            // when data to send is still remaining: 

            // data greater than the stream buffer size are written by calling the Flush function in multiple times

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

            // write the _p string into the stream buffer
            if ((error = _writer.Write(_p, size) == SFERR_NO_ERROR) {

                _p += size;

                // perform flush: send data from the stream buffer to the socket actually
                // * the flush result will be notified to the OnFlush function
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
        }
        else { 
            // when all data has been sent: 

            // release the output stream for sending data to the socket
            _writer.Release();

            // start to receive

            // get the output stream for sending data to the socket
            // * since the size argument is specified,  the stream buffer size is fixed (buffer size: 1024)
            if ((error = _socket.GetStreamReader(_bufferSize, &_reader)) == SFERR_NO_ERROR) {

                // perform fetch: receive data from the socket into the stream buffer actually
                // * the fetch result will be notified to the OnFetch function
                error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
            }
        }

    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // release the input stream for receiving data from the socket
        _reader.Release(); 
        // release the output stream for sending data to the socket
        _writer.Release();
        // close the socket
        _socket.Close();
    }
    return;
}

// callback function notified of the fetch result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // read data from the stream buffer into the string variable
        if ((error = _reader.ReadSFXAnsiString(&string)) != SFERR_NO_ERROR) { 

            _receiveString += string;

            if (!_reader.Ends()) { 
            // if data to be received is still remaining: 

                // data greater than the stream buffer size are received by calling the Fetch function in multiple times

                // perform fetch: receive data from the socket into the stream buffer actually
                // * the fetch result will be notified to the OnFetch function
                error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch));
            } else {
                // if all data has been received: 

                // display the received data on BREW Output Window
                TRACE("--------");
                TRACE("%s", _receiveString.GetCString());
                TRACE("--------");

                // release the input stream for receiving data from the socket
                _reader.Release();
                // close the socket
                _socket.Close();
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // release the input stream for receiving data from the socket
        _reader.Release(); 
        // close the socket
        _socket.Close();
    }
    return;
}
[Note] TCP Socket Communication with Fixed Buffer Stream

To receive data bigger than the stream buffer size, you have to call the SFXStreamReader::Fetch function and the SFXAnsiStringStreamReader::ReadSFXAnsiString function in multiple times. Every time the SFXStreamReader::Fetch function is called, the OnFetch callback function will be called.

On the other hand, to send data bigger than the stream buffer size, you have to call the SFXStreamWriter::Write function and the SFXStreamWriter::Flush function in multiple times. Every time the SFXStreamWriter::Flush function is called, the OnFlush callback function will be booted up.