diff options
author | Max Horn | 2008-07-29 17:42:19 +0000 |
---|---|---|
committer | Max Horn | 2008-07-29 17:42:19 +0000 |
commit | aaa4d104f7eb4094ec6bd69ef2d44f80edd61aa4 (patch) | |
tree | f515c39e140b5ddd37f46060578d3b41759a15e3 | |
parent | 0a95a4814cbc9453f3af7abb46d6998409d96f53 (diff) | |
download | scummvm-rg350-aaa4d104f7eb4094ec6bd69ef2d44f80edd61aa4.tar.gz scummvm-rg350-aaa4d104f7eb4094ec6bd69ef2d44f80edd61aa4.tar.bz2 scummvm-rg350-aaa4d104f7eb4094ec6bd69ef2d44f80edd61aa4.zip |
Added two new classes, BufferedReadStream & BufferedSeekableReadStream, as proposed on scummvm-devel
svn-id: r33419
-rw-r--r-- | common/stream.cpp | 81 | ||||
-rw-r--r-- | common/stream.h | 47 | ||||
-rw-r--r-- | test/common/bufferedreadstream.h | 27 | ||||
-rw-r--r-- | test/common/bufferedseekablereadstream.h | 65 | ||||
-rw-r--r-- | test/common/seekablesubreadstream.h | 20 | ||||
-rw-r--r-- | test/common/subreadstream.h | 15 |
6 files changed, 232 insertions, 23 deletions
diff --git a/common/stream.cpp b/common/stream.cpp index 61166fd451..d068e2fc1e 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -242,4 +242,85 @@ void SeekableSubReadStream::seek(int32 offset, int whence) { _parentStream->seek(_pos); } +BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream) + : _parentStream(parentStream), + _bufSize(bufSize), + _disposeParentStream(disposeParentStream) { + + assert(parentStream); + _buf = new byte[_bufSize]; + assert(_buf); + _pos = _bufSize = bufSize; +} +BufferedReadStream::~BufferedReadStream() { + if (_disposeParentStream) + delete _parentStream; + delete _buf; +} + +uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) { + uint32 alreadyRead = 0; + const uint32 bufBytesLeft = _bufSize - _pos; + + // Check whether the data left in the buffer suffices.... + if (dataSize > bufBytesLeft) { + // Nope, we need to read more data + + // First, flush the buffer, if it is non-empty + if (0 < bufBytesLeft) { + memcpy(dataPtr, _buf + _pos, bufBytesLeft); + _pos = _bufSize; + alreadyRead += bufBytesLeft; + dataPtr = (byte *)dataPtr + bufBytesLeft; + dataSize -= bufBytesLeft; + } + + // At this point the buffer is empty. Now if the read request + // exceeds the buffer size, just satisfy it directly. + if (dataSize > _bufSize) + return alreadyRead + _parentStream->read(dataPtr, dataSize); + + // Refill the buffer. + uint32 bytesRead = _parentStream->read(_buf, _bufSize); + _pos = 0; + + // If we didn't read as many bytes as requested, the reason + // is EOF or an error. In that case we truncate the buffer + // size, as well as the number of bytes we are going to + // return to the caller. + if (_bufSize > bytesRead) { + _bufSize = bytesRead; + if (dataSize > bytesRead) + dataSize = bytesRead; + } + } + + // Satisfy the request from the buffer + memcpy(dataPtr, _buf + _pos, dataSize); + _pos += dataSize; + return alreadyRead + dataSize; +} + +BufferedSeekableReadStream::BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream) + : BufferedReadStream(parentStream, bufSize, disposeParentStream), + _parentStream(parentStream) { +} + +void BufferedSeekableReadStream::seek(int32 offset, int whence) { + // If it is a "local" seek, we may get away with "seeking" around + // in the buffer only. + // Note: We could try to handle SEEK_END and SEEK_SET, too, but + // since they are rarely used, it seems not worth the effort. + if (whence == SEEK_CUR && (int)_pos + offset >= 0 && _pos + offset <= _bufSize) { + _pos += offset; + } else { + // Seek was not local enough, so we reset the buffer and + // just seeks normally in the parent stream. + if (whence == SEEK_CUR) + offset -= (_bufSize - _pos); + _pos = _bufSize; + _parentStream->seek(offset, whence); + } +} + } // End of namespace Common diff --git a/common/stream.h b/common/stream.h index 4cf5fae114..9f1e091604 100644 --- a/common/stream.h +++ b/common/stream.h @@ -350,15 +350,17 @@ public: class SubReadStream : virtual public ReadStream { protected: ReadStream *_parentStream; + bool _disposeParentStream; uint32 _pos; uint32 _end; - bool _disposeParentStream; public: SubReadStream(ReadStream *parentStream, uint32 end, bool disposeParentStream = false) : _parentStream(parentStream), _pos(0), _end(end), - _disposeParentStream(disposeParentStream) {} + _disposeParentStream(disposeParentStream) { + assert(parentStream); + } ~SubReadStream() { if (_disposeParentStream) delete _parentStream; } @@ -414,6 +416,47 @@ public: } }; +/** + * Wrapper class which adds buffering to any given ReadStream. + * Users can specify how big the buffer should be, and whether the + * wrapped stream should be disposed when the wrapper is disposed. + */ +class BufferedReadStream : virtual public ReadStream { +protected: + ReadStream *_parentStream; + bool _disposeParentStream; + byte *_buf; + uint32 _pos; + uint32 _bufSize; + +public: + BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false); + ~BufferedReadStream(); + + virtual bool eos() const { return (_pos == _bufSize) && _parentStream->eos(); } + virtual bool ioFailed() const { return _parentStream->ioFailed(); } + virtual void clearIOFailed() { _parentStream->clearIOFailed(); } + + virtual uint32 read(void *dataPtr, uint32 dataSize); +}; + +/** + * Wrapper class which adds buffering to any given SeekableReadStream. + * @see BufferedReadStream + */ +class BufferedSeekableReadStream : public BufferedReadStream, public SeekableReadStream { +protected: + SeekableReadStream *_parentStream; +public: + BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false); + + virtual uint32 pos() const { return _parentStream->pos() - (_bufSize - _pos); } + virtual uint32 size() const { return _parentStream->size(); } + + virtual void seek(int32 offset, int whence = SEEK_SET); +}; + + /** * Simple memory based 'stream', which implements the ReadStream interface for diff --git a/test/common/bufferedreadstream.h b/test/common/bufferedreadstream.h new file mode 100644 index 0000000000..7733949d9a --- /dev/null +++ b/test/common/bufferedreadstream.h @@ -0,0 +1,27 @@ +#include <cxxtest/TestSuite.h> + +#include "common/stream.h" + +class BufferedReadStreamTestSuite : public CxxTest::TestSuite { + public: + void test_traverse(void) { + byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + Common::MemoryReadStream ms(contents, 10); + + // Use a buffer size of 4 -- note that 10 % 4 != 0, + // so we test what happens if the cache can't be completly + // refilled. + Common::BufferedReadStream srs(&ms, 4); + + int i; + byte b; + for (i = 0; i < 10; ++i) { + TS_ASSERT( !srs.eos() ); + + b = srs.readByte(); + TS_ASSERT_EQUALS( i, b ); + } + + TS_ASSERT( srs.eos() ); + } +}; diff --git a/test/common/bufferedseekablereadstream.h b/test/common/bufferedseekablereadstream.h new file mode 100644 index 0000000000..63941904cd --- /dev/null +++ b/test/common/bufferedseekablereadstream.h @@ -0,0 +1,65 @@ +#include <cxxtest/TestSuite.h> + +#include "common/stream.h" + +class BufferedSeekableReadStreamTestSuite : public CxxTest::TestSuite { + public: + void test_traverse(void) { + byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + Common::MemoryReadStream ms(contents, 10); + + Common::BufferedSeekableReadStream ssrs(&ms, 4); + + int i; + byte b; + for (i = 0; i < 10; ++i) { + TS_ASSERT( !ssrs.eos() ); + + TS_ASSERT_EQUALS( i, ssrs.pos() ); + + ssrs.read(&b, 1); + TS_ASSERT_EQUALS( i, b ); + } + + TS_ASSERT( ssrs.eos() ); + } + + void test_seek(void) { + byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + Common::MemoryReadStream ms(contents, 10); + + Common::BufferedSeekableReadStream ssrs(&ms, 4); + byte b; + + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)0 ); + + ssrs.seek(1, SEEK_SET); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)1 ); + b = ssrs.readByte(); + TS_ASSERT_EQUALS( b, 1 ); + + ssrs.seek(5, SEEK_CUR); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)7 ); + b = ssrs.readByte(); + TS_ASSERT_EQUALS( b, 7 ); + + ssrs.seek(-3, SEEK_CUR); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)5 ); + b = ssrs.readByte(); + TS_ASSERT_EQUALS( b, 5 ); + + ssrs.seek(0, SEEK_END); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)10 ); + TS_ASSERT( ssrs.eos() ); + + ssrs.seek(3, SEEK_END); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)7 ); + b = ssrs.readByte(); + TS_ASSERT_EQUALS( b, 7 ); + + ssrs.seek(8, SEEK_END); + TS_ASSERT_EQUALS( ssrs.pos(), (uint32)2 ); + b = ssrs.readByte(); + TS_ASSERT_EQUALS( b, 2 ); + } +}; diff --git a/test/common/seekablesubreadstream.h b/test/common/seekablesubreadstream.h index c4b21667c7..4e517093a5 100644 --- a/test/common/seekablesubreadstream.h +++ b/test/common/seekablesubreadstream.h @@ -2,22 +2,19 @@ #include "common/stream.h" -class SeekableSubReadStreamTestSuite : public CxxTest::TestSuite -{ +class SeekableSubReadStreamTestSuite : public CxxTest::TestSuite { public: - void test_traverse( void ) - { + void test_traverse(void) { byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10); + Common::MemoryReadStream ms(contents, 10); int start = 2, end = 8; - Common::SeekableSubReadStream ssrs = Common::SeekableSubReadStream(&ms, start, end); + Common::SeekableSubReadStream ssrs(&ms, start, end); int i; byte b; - for (i = start; i < end; ++i) - { + for (i = start; i < end; ++i) { TS_ASSERT( !ssrs.eos() ); TS_ASSERT_EQUALS( uint32(i - start), ssrs.pos() ); @@ -29,12 +26,11 @@ class SeekableSubReadStreamTestSuite : public CxxTest::TestSuite TS_ASSERT( ssrs.eos() ); } - void test_seek( void ) - { + void test_seek(void) { byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10); + Common::MemoryReadStream ms(contents, 10); - Common::SeekableSubReadStream ssrs = Common::SeekableSubReadStream(&ms, 1, 9); + Common::SeekableSubReadStream ssrs(&ms, 1, 9); byte b; TS_ASSERT_EQUALS( ssrs.pos(), (uint32)0 ); diff --git a/test/common/subreadstream.h b/test/common/subreadstream.h index c48f57c3a8..4e14448c06 100644 --- a/test/common/subreadstream.h +++ b/test/common/subreadstream.h @@ -2,25 +2,22 @@ #include "common/stream.h" -class SubReadStreamTestSuite : public CxxTest::TestSuite -{ +class SubReadStreamTestSuite : public CxxTest::TestSuite { public: - void test_traverse( void ) - { + void test_traverse(void) { byte contents[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - Common::MemoryReadStream ms = Common::MemoryReadStream(contents, 10); + Common::MemoryReadStream ms(contents, 10); int end = 5; - Common::SubReadStream srs = Common::SubReadStream(&ms, end); + Common::SubReadStream srs(&ms, end); int i; byte b; - for (i = 0; i < end; ++i) - { + for (i = 0; i < end; ++i) { TS_ASSERT( !srs.eos() ); - srs.read(&b, 1); + b = srs.readByte(); TS_ASSERT_EQUALS( i, b ); } |