mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
Added two new classes, BufferedReadStream & BufferedSeekableReadStream, as proposed on scummvm-devel
svn-id: r33419
This commit is contained in:
@@ -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
|
||||
|
||||
+45
-2
@@ -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
|
||||
|
||||
@@ -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() );
|
||||
}
|
||||
};
|
||||
@@ -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 );
|
||||
}
|
||||
};
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user