Notes on Streams
The LTIOStreamInf class, and streams derived from it, provide an abstraction for performing I/O in a variety of ways, including "large file" I/O, buffered I/O, memory-based I/O, etc. As it is a well-known model, the semantics of the stream operations are very similar to those of the Unix stdio operations.
This technical note provides some technical details on the LTIOStreamInf operations.
Offsets
The term "offset n" refers to byte (n+1) in the file; that is, offset 0 is the first byte and offset 10 is the eleventh byte. When a stream is "positioned at offset n", we mean than the next byte read in will be byte (n+1).
Initialization
Each derived class has constructor which has no parameters and an initialize() function which zero or more parameters which will vary according to each derived class. (Note: this initialization process is different from that of most of the other SDK functions, which put all constructor parameters in the constructor and not the initialization function.)
The initialize() function must be called prior to any other member functions.
open()
- open() must be called before any other stream functions can be called (excluding initialize() and close())
- beyond that, the semantics of open() are undefined; typically, it will allocate resources on behalf of the stream, e.g. a FILE handle, and/or make them available to the user
- after open(), the stream will be positioned at offset 0 and the EOF flag will be false
- calling open() on an already opened file will return an error
close()
- close() will deallocate the resources, but in such a way that a subsequent call to open() will restore them for use
- calling close() on a closed stream will have no effect (and is not an error)
- strictly speaking, close() need not be called as the destructor is expected to call close(); relying on this is considered bad form, however
- a closed stream must be opened again before any other functions may be called
read(lt_uint8 *buffer, lt_uint32 len)
- read() will return the number of bytes successfully read; only that many bytes are valid within the read buffer
- if the number of bytes read is not equal to the number of bytes asked for, then exactly one of the following is true:
- EOF was encountered
- the stream uses "socket semantics", and one or more additional reads will be required to get the remaining desired bytes
- an error occurred
- the getLastError() function is used to determine the precise error condition, if the number of bytes read is not equal to the number of bytes requested
- the position of the stream after the read is equal to the position of the stream prior to the read plus the number of bytes successfully read
- if EOF is true when the read is requested, read will return 0 bytes read and keep EOF set to true
write(const lt_uint8 *buffer, lt_uint32 len)
- write() will return the number of bytes successfully written
- if the number of bytes written is not equal to the number of bytes requested, then exactly one of the following is true:
- the stream uses "socket semantics", and one or more additional writes will be required to output the remaining bytes
- an error occurred
- the getLastError() function is used to determine the precise error condition, if bytes read != bytes given
- the position of the stream after the write is equal to the position of the stream prior to the write plus the number of bytes successfully written
- a call to write() will always clear the EOF flag; write() never sets the EOF flag
tell()
- tell() returns the current offset as a 64-bit value
seek()
- seek() positions the stream to the given offset using a 64-bit value
- the EOF flag is reset
EOF
- when an attempt is made to read past the last byte of the file, the EOF flag becomes true
- in particular, note that merely reading the last byte will not set EOF to true
- for example, consider a file of 4 bytes, with the stream positioned at offset 0:
- a read request of 4 bytes will return 4 bytes read, position is offset 4, EOF is not set
- a read request of 6 bytes will return 4 bytes read, position is offset 4, EOF is set
- a write operation has no effect on the EOF flag
- a seek operation always clears the EOF flag
duplicate()
- duplicate() creates a new stream of the same type as the original stream and calls initialize() on it with same parameters as original stream
- isOpen() should initially return false; it is up to the caller to call open() on the newly created stream
getLastError()
- The getLastError() function is used to get the status code when one of the following I/O functions failed:
- read()
- write()
- tell()
- duplicate()
- The getLastError() function is required because, like the other I/O functions, these functions do not return status codes.
- The value returned by getLastError() is undefined unless called immediately after a failed call to one of the above functions. A call to any other I/O function will invalidate the state of getLastError().
- The minimal implementation of this function is to return LT_STS_Failure.
Modes
Any "modes" that a stream supports ("w", "wb", "r+", etc) are defined by the derived class; there is no notion of mode at the base class level.
For example, it is entirely possible one would want to make a "read-only file stream" class. Such a class would be implemented with the write() function always returning 0 bytes read.