Allow buffered stream to work with an offset.

If the stream being buffered is buffered from somewhere other than
the start, the SkFrontBufferedStream needs to take that into account
when reporting its length.

R=djsollen@google.com, bungeman@google.com, reed@google.com

Author: scroggo@google.com

Review URL: https://codereview.chromium.org/157103002

git-svn-id: http://skia.googlecode.com/svn/trunk@13388 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-02-10 22:03:21 +00:00
parent ba9354b9d4
commit 74b88b70b8
2 changed files with 100 additions and 11 deletions

View File

@ -24,14 +24,16 @@ public:
virtual size_t getPosition() const SK_OVERRIDE { return fOffset; }
virtual bool hasLength() const SK_OVERRIDE;
virtual bool hasLength() const SK_OVERRIDE { return fHasLength; }
virtual size_t getLength() const SK_OVERRIDE;
virtual size_t getLength() const SK_OVERRIDE { return fLength; }
virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
private:
SkAutoTUnref<SkStream> fStream;
const bool fHasLength;
const size_t fLength;
// Current offset into the stream. Always >= 0.
size_t fOffset;
// Amount that has been buffered by calls to read. Will always be less than
@ -70,6 +72,8 @@ SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t buffe
FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
: fStream(SkRef(stream))
, fHasLength(stream->hasPosition() && stream->hasLength())
, fLength(stream->getLength() - stream->getPosition())
, fOffset(0)
, fBufferedSoFar(0)
, fBufferSize(bufferSize)
@ -94,14 +98,6 @@ bool FrontBufferedStream::rewind() {
return false;
}
bool FrontBufferedStream::hasLength() const {
return fStream->hasLength();
}
size_t FrontBufferedStream::getLength() const {
return fStream->getLength();
}
size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
SkASSERT(fOffset < fBufferedSoFar);
// Some data has already been copied to fBuffer. Read up to the

View File

@ -16,7 +16,7 @@ static void test_read(skiatest::Reporter* reporter, SkStream* bufferedStream,
// output for reading bufferedStream.
SkAutoMalloc storage(bytesToRead);
size_t bytesRead = bufferedStream->read(storage.get(), bytesToRead);
const size_t bytesRead = bufferedStream->read(storage.get(), bytesToRead);
REPORTER_ASSERT(reporter, bytesRead == bytesToRead || bufferedStream->isAtEnd());
REPORTER_ASSERT(reporter, memcmp(storage.get(), expectations, bytesRead) == 0);
}
@ -27,6 +27,20 @@ static void test_rewind(skiatest::Reporter* reporter,
REPORTER_ASSERT(reporter, success == shouldSucceed);
}
// Test that hasLength() returns the correct value, based on the stream
// being wrapped. A length can only be known if the wrapped stream has a
// length and it has a position (so its initial position can be taken into
// account when computing the length).
static void test_hasLength(skiatest::Reporter* reporter,
const SkStream& bufferedStream,
const SkStream& streamBeingBuffered) {
if (streamBeingBuffered.hasLength() && streamBeingBuffered.hasPosition()) {
REPORTER_ASSERT(reporter, bufferedStream.hasLength());
} else {
REPORTER_ASSERT(reporter, !bufferedStream.hasLength());
}
}
// All tests will buffer this string, and compare output to the original.
// The string is long to ensure that all of our lengths being tested are
// smaller than the string length.
@ -38,6 +52,7 @@ static void test_incremental_buffering(skiatest::Reporter* reporter, size_t buff
SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
test_hasLength(reporter, *bufferedStream.get(), memStream);
// First, test reading less than the max buffer size.
test_read(reporter, bufferedStream, gAbcs, bufferSize / 2);
@ -64,6 +79,7 @@ static void test_incremental_buffering(skiatest::Reporter* reporter, size_t buff
static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t bufferSize) {
SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
test_hasLength(reporter, *bufferedStream.get(), memStream);
// Read exactly the amount that fits in the buffer.
test_read(reporter, bufferedStream, gAbcs, bufferSize);
@ -82,6 +98,7 @@ static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t buf
static void test_skipping(skiatest::Reporter* reporter, size_t bufferSize) {
SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
test_hasLength(reporter, *bufferedStream.get(), memStream);
// Skip half the buffer.
bufferedStream->skip(bufferSize / 2);
@ -134,6 +151,7 @@ static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferS
// Create a buffer that matches the length of the stream.
SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
test_hasLength(reporter, *bufferedStream.get(), memStream);
// Attempt to read one more than the bufferSize
test_read(reporter, bufferedStream.get(), gAbcs, bufferSize + 1);
@ -143,11 +161,86 @@ static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferS
test_read(reporter, bufferedStream, gAbcs, bufferSize);
}
// Dummy stream that optionally has a length and/or position. Tests that FrontBufferedStream's
// length depends on the stream it's buffering having a length and position.
class LengthOptionalStream : public SkStream {
public:
LengthOptionalStream(bool hasLength, bool hasPosition)
: fHasLength(hasLength)
, fHasPosition(hasPosition)
{}
virtual bool hasLength() const SK_OVERRIDE {
return fHasLength;
}
virtual bool hasPosition() const SK_OVERRIDE {
return fHasPosition;
}
virtual size_t read(void*, size_t) SK_OVERRIDE {
return 0;
}
virtual bool isAtEnd() const SK_OVERRIDE {
return true;
}
private:
const bool fHasLength;
const bool fHasPosition;
};
// Test all possible combinations of the wrapped stream having a length and a position.
static void test_length_combos(skiatest::Reporter* reporter, size_t bufferSize) {
for (int hasLen = 0; hasLen <= 1; hasLen++) {
for (int hasPos = 0; hasPos <= 1; hasPos++) {
LengthOptionalStream stream((bool) hasLen, (bool) hasPos);
SkAutoTUnref<SkStream> buffered(SkFrontBufferedStream::Create(&stream, bufferSize));
test_hasLength(reporter, *buffered.get(), stream);
}
}
}
// Test using a stream with an initial offset.
static void test_initial_offset(skiatest::Reporter* reporter, size_t bufferSize) {
SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
// Skip a few characters into the memStream, so that bufferedStream represents an offset into
// the stream it wraps.
const size_t arbitraryOffset = 17;
memStream.skip(arbitraryOffset);
SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
// Since SkMemoryStream has a length and a position, bufferedStream must also.
REPORTER_ASSERT(reporter, bufferedStream->hasLength());
const size_t amountToRead = 10;
const size_t bufferedLength = bufferedStream->getLength();
size_t currentPosition = bufferedStream->getPosition();
REPORTER_ASSERT(reporter, 0 == currentPosition);
// Read the stream in chunks. After each read, the position must match currentPosition,
// which sums the amount attempted to read, unless the end of the stream has been reached.
// Importantly, the end should not have been reached until currentPosition == bufferedLength.
while (currentPosition < bufferedLength) {
REPORTER_ASSERT(reporter, !bufferedStream->isAtEnd());
test_read(reporter, bufferedStream, gAbcs + arbitraryOffset + currentPosition,
amountToRead);
currentPosition = SkTMin(currentPosition + amountToRead, bufferedLength);
REPORTER_ASSERT(reporter, bufferedStream->getPosition() == currentPosition);
}
REPORTER_ASSERT(reporter, bufferedStream->isAtEnd());
REPORTER_ASSERT(reporter, bufferedLength == currentPosition);
}
static void test_buffers(skiatest::Reporter* reporter, size_t bufferSize) {
test_incremental_buffering(reporter, bufferSize);
test_perfectly_sized_buffer(reporter, bufferSize);
test_skipping(reporter, bufferSize);
test_read_beyond_buffer(reporter, bufferSize);
test_length_combos(reporter, bufferSize);
test_initial_offset(reporter, bufferSize);
}
DEF_TEST(FrontBufferedStream, reporter) {