CXStream

From cxwiki

The CXStream class hierarchy provides support for readable and writeable streams, ranging from file streams and network sockets to adaptors such as data codecs. The underlying functionality is virtual, but non-virtual caching operations are provided transparently to avoid exessive virtual call overhead. As much as is possible, CXStream-derived classes provide completely generic behaviour, and CXStream-utilising functions accept a base CXStream* rather than specifying a more specialised derived type. This allows an application to create a CXStream of any type (potentially even creating something new) and pass it to an existing function or system.
 
This page documents the usage of CXStream (and its derived classes). For guidance on creating new CXStream-derived classes, see CXStream Development.
 
Any given CXStream-derived object may be used on any thread, or passed between threads, but is NOT reentrant, except where specifically noted.
 
It is expected that CXStream implementations will closely follow the expected behaviour detailed in this document. In scenarios where customised behaviour may be preferrable, that behaviour should either be exposed through additional APIs which do not affect the behaviour of the standard APIs, or via additional capabilities flags which should be explicitly enabled.
 

Read / Write

// Read from the current read cursor position in this stream. If the
// size is greater than zero, the output buffer must be non-null. The
// return value is the number of bytes successfully read.
// This function will normally block until the requested number of
// bytes have been read or until an error condition has resulted
// (eg. end-of-file, i/o error, disconnected, etc.) A non-blocking
// stream (which is not the default behaviour) will instead return
// after any non-zero number of bytes has been read, without 
// raising an error status.
StreamSize Read(void* __nullable o_buffer, StreamSize size);

// Writes to the current write cursor position in this stream. If
// the size is gerater than zero, the input buffer must be non-null.
// The return value is the number of bytes succesfully written.
// This function will normally block until the requested number of
// bytes have been written, or until an error condition has resulted
// (eg. end-of-file, i/o error, disconnected, etc.) A non-blocking
// stream (which is not the default behaviour) will instead return
// after any non-zero number of bytes has been written, without
// raising an error status.
StreamSize Write(const void* __nullable buffer, StreamSize size);

// Semantically equivalent to calling Read() with the specified
// size and then discarding the buffer. This may be more 
// performant in some implementations.
CXStream::StreamSize ReadDiscard(CXStream::StreamSize a_size);

// Semantically equivalent to calling Write(), however the
// implementation is free to write alternative data (including
// but not limited to all zeroes) or simply seek over the
// specified number of bytes. This may be used where an empty
// space is being left in the stream that is expected to be
// filled at a later time. The stream size and cursor are
// affected in the same manner as if this was a regular Write().
CXStream::StreamSize WriteDiscard(const void* __nonnull a_buffer, StreamSize a_size);

// Flush any cached or asychronous writes to the backing store.
// Since i/o error conditions (etc.) are not detectable until the
// actual output operation is attempted, it is necessary to call
// Flush() before GetStreamResult() if an accurate outcome is
// required.
void Flush(FlushFlags flags = FLUSH_FLAGS_BLOCK);

 

Cursor

// Performs a stream seek operation. This will fail if the stream does
// not specify CAPS_SEEK, and may be limited if the stream does not 
// specify CAPS_TELL or CAPS_SIZE or if it does specify CAPS_NO_REWIND.
// The typical three seek modes are available:
// * STREAM_SEEK_START - 'pos' references an absolute position within the
//   stream. ('pos' should be positive.)
// * STREAM_SEEK_CURRENT - 'pos' is an offset from the current cursor
//   position. ('pos' may be positive or negative.)
// * STREAM_SEEK_END - 'pos' is an offset from the end of the stream.
//   ('pos' should be negative.)
// Attempting to seek to a negative absolute position will leave the 
// stream at the start (position 0). Attempting to seek past the end of
// the stream will generally leave the stream at the end, but may 
// seek into 'virtual space' past the end.
// If the stream specifies CAPS_TELL, the return value is the absolute
// cursor position after the seek has completed. Otherwise, the return
// value is implementation-specific.
// Seeking may trigger a buffer flush, which may in turn cause a result
// code to be raised. This does not necessarily indicate a seek failure,
// but does indicate a failure of some kind. To differentiate, pay 
// attention to the result code and the resultant StreamPos.
StreamPos Seek(StreamPos pos, uint mode = STREAM_SEEK_START);

// Returns the stream's cursor position. If this stream does not specify
// CAPS_TELL, the result is implementation-specific.
StreamPos Tell(void) const;

// Returns the stream's current end-of-file marker position. If this
// stream does not specify CAPS_SIZE, the result is implementation-
// specific. If the stream specifies CAPS_NO_REWIND, the size is always
// relative to the current cursor position rather than the original start
// of stream.
StreamSize GetSize(void) const;

// Resizes the stream to the specified size. Not supported by all
// stream types. Any newly allocated portion of the stream will be
// effectively zeroed. If the stream size is reduced below the cursor
// position, the cursor will be moved to the new end-of-file position.
void SetSize(StreamSize size);

// Resizes the stream to the current cursor position. Resizing is
// not supported by all stream types.
void Truncate(void)

// Returns the number of bytes remaining until the current end-of-
// stream position is reached. Sets CXResultCode::NotImplemented 
// and returns zero if the result could not be determined for this 
// stream.
StreamSize GetRemain(void) const;

// Sets o_remain to the number of bytes remaining until the current
// end-of-stream position is reached. Returns true on success, or
// false if the result could not be determined for this stream.
bool GetRemain(StreamSize& o_remain) const;

// Returns true if the end-of-file flag is currently set. This
// happens after an operation partially or entirely fails due to
// reaching the end-of-file position. This is cleared by a
// successful seek operation. It is possible for the cursor to
// be at the end-of-file position without this flag being set.
bool IsEOF(void) const;

 

Capabilities Flags

enum
{
  CAPS_READ = 1,       // stream is readable
  CAPS_WRITE = 2,      // stream is writable
  CAPS_SIZE = 4,       // stream provider supports GetSize() and STREAM_SEEK_END
  CAPS_SEEK = 8,       // stream provider supports Seek() -- usually you actually want CAPS_TELL
  CAPS_TELL = 16,      // stream provider supports Tell()
  CAPS_UNIFIED_CURSOR = 32,    // stream has unified read / write cursor -- ie. Tell() is affected by both Read() and Write()
  CAPS_NO_REWIND = 64,  // stream cannot seek backwards, and GetSize() returns the number of bytes after the current position
  CAPS_SEEK_IN_WRITE_CACHE = 128, // since the original implementation did not support this, we have to flag if we support the technique.

  // "flag" caps
  CAPS_OPEN = 256,     // stream is open
  CAPS_EOF = 512,       // stream is known to be at the EOF position

  //
  CAPS_NON_BLOCKING = 1024,     // stream is in non-blocking mode
};

 

 

// Get the capabilities flags for this stream. These are defined
// by the implementation to describe how this stream functions in
// general terms.
Flags32 GetCaps(void) const;

// Request a change of capabilities for this stream. Any 
// capabilities flags included in 'mask' are to be updated. Any
// capabilities flags also included in 'caps' are to be enabled,
// the remainder are to be disabled. The implementation is free
// to partially or fully ignore this request.
void SetCaps(const Flags32& caps, const Flags32& mask);

// Request the specified capabilites to be enabled. The 
// implementation is free to partially or fully ignore this 
// request.
void SetCaps(const Flags32& caps);

// Request the specified capabilities to be disabled. The
// implementation is free to partially or fully ignore this
// request.
void ClearCaps(const Flags32& caps);

// Provide a hint to the implementation. The 'hintEnum' is a
// four-char-code identifying the specific attribute to affect.
// The 'hintValue' is a hint-specific parameter to adjust.
// The implementation is free to ignore this request. This
// mechanism is provided as a way to offer generic hints that
// may be applicable across a broad range of implementations,
// without the application having to be aware of the specifics
// of the CXStream object on which the hint is being set. 
// Because they may be ignored, hints should generally change
// performance characteristics and other similar behaviours,
// rather than changing the semantics of any APIs.
void SetHint(uint32 hintEnum, uint32 hintValue);

 

Status

// Returns the most recent CXResultCode that was raised against
// this stream. The implementation will never clear this result
// short of a complete close/reopen of the stream, or an explicit
// call to ClearStreamResult(), so this function does not need to
// be called after every stream operation.'
// Stream operations such as Write() may result only in cache 
// activity with the actual i/o operation delayed until the cache
// is flushed. In this scenario, certain classes of result code
// may not be available until the flush is performed. If a   
// guaranteed final result is required, the application should 
// call Flush() prior to calling GetStreamResult().
CXResultCode GetStreamResult(void) const;

// Explicitly clears the stream's CXResultCode.
void ClearStreamResult(void);

// Explicitly sets the stream's CXResultCode to the specified
// value. An attempt to set the stream to CXResultCode::OK is
// ignored, leaving the previous result code intact.
void SetStreamResult(CXResultCode result) const;

// If the stream already has a result code flagged, this call
// is ignored. Otherwise, the stream's result code is 
// explicitly set to the specified value.
void ConcatenateResult(CXResultCode result) const;

// Requests the implementation to shut down this stream, ready
// for another call to Open(). This mechanism is not implemented
// in the default implementation, but it is considered a generic
// enough requirement that an generic API is provided. The 
// matching Open() functionality is not considered generic and
// an implementation-specific API must be used.
void Close(void);

// Returns true if this stream is open.
bool IsOpen(void) const;

// Returns true if this stream is closed.
bool IsClosed(void) const;


 

 

Events

Events can be used to perform non-threaded IO without requiring the application to poll the stream and without introducing unwanted blocking.

enum EventType
{
  STREAM_EVENT_READ,    // Set whenever the stream has further data available to be read.
  STREAM_EVENT_WRITE,   // Set whenever the stream may write without blocking.
  STREAM_EVENT_ERROR,   // Set whenever the stream has a result code set.
};

// Returns a CXGenericEventRef for the specified event type.
CXGenericEventRef GetStreamEvent(EventType eventType) const;