michael@0: /* michael@0: * Copyright 2013 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkFrontBufferedStream.h" michael@0: #include "SkStream.h" michael@0: #include "SkTemplates.h" michael@0: michael@0: class FrontBufferedStream : public SkStreamRewindable { michael@0: public: michael@0: // Called by Create. michael@0: FrontBufferedStream(SkStream*, size_t bufferSize); michael@0: michael@0: virtual size_t read(void* buffer, size_t size) SK_OVERRIDE; michael@0: michael@0: virtual bool isAtEnd() const SK_OVERRIDE; michael@0: michael@0: virtual bool rewind() SK_OVERRIDE; michael@0: michael@0: virtual bool hasPosition() const SK_OVERRIDE { return true; } michael@0: michael@0: virtual size_t getPosition() const SK_OVERRIDE { return fOffset; } michael@0: michael@0: virtual bool hasLength() const SK_OVERRIDE { return fHasLength; } michael@0: michael@0: virtual size_t getLength() const SK_OVERRIDE { return fLength; } michael@0: michael@0: virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; } michael@0: michael@0: private: michael@0: SkAutoTUnref fStream; michael@0: const bool fHasLength; michael@0: const size_t fLength; michael@0: // Current offset into the stream. Always >= 0. michael@0: size_t fOffset; michael@0: // Amount that has been buffered by calls to read. Will always be less than michael@0: // fBufferSize. michael@0: size_t fBufferedSoFar; michael@0: // Total size of the buffer. michael@0: const size_t fBufferSize; michael@0: // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a michael@0: // NULL stream. michael@0: SkAutoTMalloc fBuffer; michael@0: michael@0: // Read up to size bytes from already buffered data, and copy to michael@0: // dst, if non-NULL. Updates fOffset. Assumes that fOffset is less michael@0: // than fBufferedSoFar. michael@0: size_t readFromBuffer(char* dst, size_t size); michael@0: michael@0: // Buffer up to size bytes from the stream, and copy to dst if non- michael@0: // NULL. Updates fOffset and fBufferedSoFar. Assumes that fOffset is michael@0: // less than fBufferedSoFar, and size is greater than 0. michael@0: size_t bufferAndWriteTo(char* dst, size_t size); michael@0: michael@0: // Read up to size bytes directly from the stream and into dst if non- michael@0: // NULL. Updates fOffset. Assumes fOffset is at or beyond the buffered michael@0: // data, and size is greater than 0. michael@0: size_t readDirectlyFromStream(char* dst, size_t size); michael@0: michael@0: typedef SkStream INHERITED; michael@0: }; michael@0: michael@0: SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) { michael@0: if (NULL == stream) { michael@0: return NULL; michael@0: } michael@0: return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize)); michael@0: } michael@0: michael@0: FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize) michael@0: : fStream(SkRef(stream)) michael@0: , fHasLength(stream->hasPosition() && stream->hasLength()) michael@0: , fLength(stream->getLength() - stream->getPosition()) michael@0: , fOffset(0) michael@0: , fBufferedSoFar(0) michael@0: , fBufferSize(bufferSize) michael@0: , fBuffer(bufferSize) {} michael@0: michael@0: bool FrontBufferedStream::isAtEnd() const { michael@0: if (fOffset < fBufferedSoFar) { michael@0: // Even if the underlying stream is at the end, this stream has been michael@0: // rewound after buffering, so it is not at the end. michael@0: return false; michael@0: } michael@0: michael@0: return fStream->isAtEnd(); michael@0: } michael@0: michael@0: bool FrontBufferedStream::rewind() { michael@0: // Only allow a rewind if we have not exceeded the buffer. michael@0: if (fOffset <= fBufferSize) { michael@0: fOffset = 0; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) { michael@0: SkASSERT(fOffset < fBufferedSoFar); michael@0: // Some data has already been copied to fBuffer. Read up to the michael@0: // lesser of the size requested and the remainder of the buffered michael@0: // data. michael@0: const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset); michael@0: if (dst != NULL) { michael@0: memcpy(dst, fBuffer + fOffset, bytesToCopy); michael@0: } michael@0: michael@0: // Update fOffset to the new position. It is guaranteed to be michael@0: // within the buffered data. michael@0: fOffset += bytesToCopy; michael@0: SkASSERT(fOffset <= fBufferedSoFar); michael@0: michael@0: return bytesToCopy; michael@0: } michael@0: michael@0: size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) { michael@0: SkASSERT(size > 0); michael@0: SkASSERT(fOffset >= fBufferedSoFar); michael@0: // Data needs to be buffered. Buffer up to the lesser of the size requested michael@0: // and the remainder of the max buffer size. michael@0: const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar); michael@0: char* buffer = fBuffer + fOffset; michael@0: const size_t buffered = fStream->read(buffer, bytesToBuffer); michael@0: michael@0: fBufferedSoFar += buffered; michael@0: fOffset = fBufferedSoFar; michael@0: SkASSERT(fBufferedSoFar <= fBufferSize); michael@0: michael@0: // Copy the buffer to the destination buffer and update the amount read. michael@0: if (dst != NULL) { michael@0: memcpy(dst, buffer, buffered); michael@0: } michael@0: michael@0: return buffered; michael@0: } michael@0: michael@0: size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) { michael@0: SkASSERT(size > 0); michael@0: // If we get here, we have buffered all that can be buffered. michael@0: SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize); michael@0: michael@0: const size_t bytesReadDirectly = fStream->read(dst, size); michael@0: fOffset += bytesReadDirectly; michael@0: michael@0: // If we have read past the end of the buffer, rewinding is no longer michael@0: // supported, so we can go ahead and free the memory. michael@0: if (bytesReadDirectly > 0) { michael@0: fBuffer.reset(0); michael@0: } michael@0: michael@0: return bytesReadDirectly; michael@0: } michael@0: michael@0: size_t FrontBufferedStream::read(void* voidDst, size_t size) { michael@0: // Cast voidDst to a char* for easy addition. michael@0: char* dst = reinterpret_cast(voidDst); michael@0: SkDEBUGCODE(const size_t totalSize = size;) michael@0: const size_t start = fOffset; michael@0: michael@0: // First, read any data that was previously buffered. michael@0: if (fOffset < fBufferedSoFar) { michael@0: const size_t bytesCopied = this->readFromBuffer(dst, size); michael@0: michael@0: // Update the remaining number of bytes needed to read michael@0: // and the destination buffer. michael@0: size -= bytesCopied; michael@0: SkASSERT(size + (fOffset - start) == totalSize); michael@0: if (dst != NULL) { michael@0: dst += bytesCopied; michael@0: } michael@0: } michael@0: michael@0: // Buffer any more data that should be buffered, and copy it to the michael@0: // destination. michael@0: if (size > 0 && fBufferedSoFar < fBufferSize) { michael@0: const size_t buffered = this->bufferAndWriteTo(dst, size); michael@0: michael@0: // Update the remaining number of bytes needed to read michael@0: // and the destination buffer. michael@0: size -= buffered; michael@0: SkASSERT(size + (fOffset - start) == totalSize); michael@0: if (dst != NULL) { michael@0: dst += buffered; michael@0: } michael@0: } michael@0: michael@0: if (size > 0 && !fStream->isAtEnd()) { michael@0: SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size); michael@0: SkDEBUGCODE(size -= bytesReadDirectly;) michael@0: SkASSERT(size + (fOffset - start) == totalSize); michael@0: } michael@0: michael@0: return fOffset - start; michael@0: }