return this->read(NULL, size);
}
+ /**
+ * Attempt to peek at size bytes.
+ * If this stream supports peeking, and it can peek size bytes, copy size
+ * bytes into buffer, and return true.
+ * If the stream does not support peeking, or cannot peek size bytes,
+ * return false and leave buffer unchanged.
+ * The stream is guaranteed to be in the same visible state after this
+ * call, regardless of success or failure.
+ * @param buffer Must not be NULL. Destination to copy bytes.
+ * @param size Number of bytes to copy.
+ * @return Whether the peek was performed.
+ */
+ virtual bool peek(void* /* buffer */, size_t /* size */) const { return false; }
+
/** Returns true when all the bytes in the stream have been read.
* This may return true early (when there are no more bytes to be read)
* or late (after the first unsuccessful read).
size_t read(void* buffer, size_t size) override;
bool isAtEnd() const override;
+ bool peek(void* buffer, size_t size) const override;
+
bool rewind() override;
SkMemoryStream* duplicate() const override;
return size;
}
+bool SkMemoryStream::peek(void* buffer, size_t size) const {
+ SkASSERT(buffer != NULL);
+ const size_t position = fOffset;
+ if (size > fData->size() - position) {
+ // The stream is not large enough to satisfy this request.
+ return false;
+ }
+ SkMemoryStream* nonConstThis = const_cast<SkMemoryStream*>(this);
+ SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(buffer, size);
+ SkASSERT(bytesRead == size);
+ nonConstThis->fOffset = position;
+ return true;
+}
+
bool SkMemoryStream::isAtEnd() const {
return fOffset == fData->size();
}
size_t read(void* buffer, size_t size) override;
+ bool peek(void* buffer, size_t size) const override;
+
bool isAtEnd() const override;
bool rewind() override;
return bytesReadDirectly;
}
+bool FrontBufferedStream::peek(void* dst, size_t size) const {
+ // Keep track of the offset so we can return to it.
+ const size_t start = fOffset;
+ if (start + size > fBufferSize) {
+ // This stream is not able to buffer enough.
+ return false;
+ }
+ FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
+ SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(dst, size);
+ SkASSERT(bytesRead == size);
+ nonConstThis->fOffset = start;
+ return true;
+}
+
size_t FrontBufferedStream::read(void* voidDst, size_t size) {
// Cast voidDst to a char* for easy addition.
char* dst = reinterpret_cast<char*>(voidDst);
* found in the LICENSE file.
*/
+#include "Resources.h"
#include "SkData.h"
+#include "SkFrontBufferedStream.h"
#include "SkOSFile.h"
#include "SkRandom.h"
#include "SkStream.h"
TestPackedUInt(reporter);
TestNullData();
}
+
+/**
+ * Tests peeking and then reading the same amount. The two should provide the
+ * same results.
+ * Returns whether the stream could peek.
+ */
+static bool compare_peek_to_read(skiatest::Reporter* reporter,
+ SkStream* stream, size_t bytesToPeek) {
+ // The rest of our tests won't be very interesting if bytesToPeek is zero.
+ REPORTER_ASSERT(reporter, bytesToPeek > 0);
+ SkAutoMalloc peekStorage(bytesToPeek);
+ SkAutoMalloc readStorage(bytesToPeek);
+ void* peekPtr = peekStorage.get();
+ void* readPtr = peekStorage.get();
+
+ if (!stream->peek(peekPtr, bytesToPeek)) {
+ return false;
+ }
+ const size_t bytesRead = stream->read(readPtr, bytesToPeek);
+
+ // bytesRead should only be less than attempted if the stream is at the
+ // end.
+ REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
+
+ // peek and read should behave the same, except peek returned to the
+ // original position, so they read the same data.
+ REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesRead));
+
+ return true;
+}
+
+static void test_peeking_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
+ size_t peeked = 0;
+ for (size_t i = 1; !stream->isAtEnd(); i++) {
+ const bool couldPeek = compare_peek_to_read(r, stream, i);
+ if (!couldPeek) {
+ REPORTER_ASSERT(r, peeked + i > limit);
+ // No more peeking is supported.
+ break;
+ }
+ peeked += i;
+ }
+}
+
+static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
+ const SkStream& original,
+ size_t bufferSize) {
+ SkStream* dupe = original.duplicate();
+ REPORTER_ASSERT(r, dupe != NULL);
+ SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
+ REPORTER_ASSERT(r, bufferedStream != NULL);
+ test_peeking_stream(r, bufferedStream, bufferSize);
+}
+
+DEF_TEST(StreamPeek, reporter) {
+ // Test a memory stream.
+ const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
+ SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
+ test_peeking_stream(reporter, &memStream, memStream.getLength());
+
+ // Test an arbitrary file stream. file streams do not support peeking.
+ SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
+ REPORTER_ASSERT(reporter, fileStream.isValid());
+ SkAutoMalloc storage(fileStream.getLength());
+ for (size_t i = 1; i < fileStream.getLength(); i++) {
+ REPORTER_ASSERT(reporter, !fileStream.peek(storage.get(), i));
+ }
+
+ // Now test some FrontBufferedStreams
+ for (size_t i = 1; i < memStream.getLength(); i++) {
+ test_peeking_front_buffered_stream(reporter, memStream, i);
+ }
+}