| /* | |
| * Copyright 2013 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/codec/SkCodec.h" | |
| #include "include/core/SkStream.h" | |
| #include "FrontBufferedStream.h" | |
| #include <algorithm> | |
| #include <memory> | |
| namespace { | |
| class FrontBufferedStream : public SkStreamRewindable { | |
| public: | |
| // Called by Make. | |
| FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize); | |
| ~FrontBufferedStream() override; | |
| bool failedToAllocateBuffer() const { return !fBuffer; } | |
| size_t read(void* buffer, size_t size) override; | |
| size_t peek(void* buffer, size_t size) const override; | |
| bool isAtEnd() const override; | |
| bool rewind() override; | |
| bool hasLength() const override { return fHasLength; } | |
| size_t getLength() const override { return fLength; } | |
| private: | |
| SkStreamRewindable* onDuplicate() const override { return nullptr; } | |
| std::unique_ptr<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 | |
| // fBufferSize. | |
| size_t fBufferedSoFar; | |
| // Total size of the buffer. | |
| const size_t fBufferSize; | |
| char* fBuffer; | |
| inline static constexpr size_t kStorageSize = SkCodec::MinBufferedBytesNeeded(); | |
| char fStorage[kStorageSize]; | |
| // Read up to size bytes from already buffered data, and copy to | |
| // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less | |
| // than fBufferedSoFar. | |
| size_t readFromBuffer(char* dst, size_t size); | |
| // Buffer up to size bytes from the stream, and copy to dst if non- | |
| // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is | |
| // less than fBufferedSoFar, and size is greater than 0. | |
| size_t bufferAndWriteTo(char* dst, size_t size); | |
| // Read up to size bytes directly from the stream and into dst if non- | |
| // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered | |
| // data, and size is greater than 0. | |
| size_t readDirectlyFromStream(char* dst, size_t size); | |
| using INHERITED = SkStream; | |
| }; | |
| } // anonymous namespace | |
| namespace android { | |
| namespace skia { | |
| std::unique_ptr<SkStreamRewindable> FrontBufferedStream::Make(std::unique_ptr<SkStream> stream, | |
| size_t bufferSize) { | |
| if (!stream) { | |
| return nullptr; | |
| } | |
| auto frontBufferedStream = std::make_unique<::FrontBufferedStream>( | |
| std::move(stream), bufferSize); | |
| if (frontBufferedStream->failedToAllocateBuffer()) { | |
| return nullptr; | |
| } | |
| return frontBufferedStream; | |
| } | |
| } // namespace skia | |
| } // namespace android | |
| namespace { | |
| FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize) | |
| : fStream(std::move(stream)) | |
| , fHasLength(fStream->hasPosition() && fStream->hasLength()) | |
| , fLength(fStream->getLength() - fStream->getPosition()) | |
| , fOffset(0) | |
| , fBufferedSoFar(0) | |
| , fBufferSize(bufferSize) | |
| , fBuffer(bufferSize <= kStorageSize ? fStorage | |
| : reinterpret_cast<char*>(malloc(bufferSize))) {} | |
| FrontBufferedStream::~FrontBufferedStream() { | |
| if (fBuffer != fStorage) { | |
| free(fBuffer); | |
| } | |
| } | |
| bool FrontBufferedStream::isAtEnd() const { | |
| if (fOffset < fBufferedSoFar) { | |
| // Even if the underlying stream is at the end, this stream has been | |
| // rewound after buffering, so it is not at the end. | |
| return false; | |
| } | |
| return fStream->isAtEnd(); | |
| } | |
| bool FrontBufferedStream::rewind() { | |
| // Only allow a rewind if we have not exceeded the buffer. | |
| if (fOffset <= fBufferSize) { | |
| fOffset = 0; | |
| return true; | |
| } | |
| return false; | |
| } | |
| size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) { | |
| SkASSERT(fOffset < fBufferedSoFar); | |
| // Some data has already been copied to fBuffer. Read up to the | |
| // lesser of the size requested and the remainder of the buffered | |
| // data. | |
| const size_t bytesToCopy = std::min(size, fBufferedSoFar - fOffset); | |
| if (dst != nullptr) { | |
| memcpy(dst, fBuffer + fOffset, bytesToCopy); | |
| } | |
| // Update fOffset to the new position. It is guaranteed to be | |
| // within the buffered data. | |
| fOffset += bytesToCopy; | |
| SkASSERT(fOffset <= fBufferedSoFar); | |
| return bytesToCopy; | |
| } | |
| size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) { | |
| SkASSERT(size > 0); | |
| SkASSERT(fOffset >= fBufferedSoFar); | |
| SkASSERT(fBuffer); | |
| // Data needs to be buffered. Buffer up to the lesser of the size requested | |
| // and the remainder of the max buffer size. | |
| const size_t bytesToBuffer = std::min(size, fBufferSize - fBufferedSoFar); | |
| char* buffer = fBuffer + fOffset; | |
| const size_t buffered = fStream->read(buffer, bytesToBuffer); | |
| fBufferedSoFar += buffered; | |
| fOffset = fBufferedSoFar; | |
| SkASSERT(fBufferedSoFar <= fBufferSize); | |
| // Copy the buffer to the destination buffer and update the amount read. | |
| if (dst != nullptr) { | |
| memcpy(dst, buffer, buffered); | |
| } | |
| return buffered; | |
| } | |
| size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) { | |
| SkASSERT(size > 0); | |
| // If we get here, we have buffered all that can be buffered. | |
| SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize); | |
| const size_t bytesReadDirectly = fStream->read(dst, size); | |
| fOffset += bytesReadDirectly; | |
| // If we have read past the end of the buffer, rewinding is no longer | |
| // supported, so we can go ahead and free the memory. | |
| if (bytesReadDirectly > 0 && fBuffer != fStorage) { | |
| free(fBuffer); | |
| fBuffer = nullptr; | |
| } | |
| return bytesReadDirectly; | |
| } | |
| size_t 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 >= fBufferSize) { | |
| // This stream is not able to buffer. | |
| return 0; | |
| } | |
| size = std::min(size, fBufferSize - start); | |
| FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this); | |
| const size_t bytesRead = nonConstThis->read(dst, size); | |
| nonConstThis->fOffset = start; | |
| return bytesRead; | |
| } | |
| size_t FrontBufferedStream::read(void* voidDst, size_t size) { | |
| // Cast voidDst to a char* for easy addition. | |
| char* dst = reinterpret_cast<char*>(voidDst); | |
| SkDEBUGCODE(const size_t totalSize = size;) | |
| const size_t start = fOffset; | |
| // First, read any data that was previously buffered. | |
| if (fOffset < fBufferedSoFar) { | |
| const size_t bytesCopied = this->readFromBuffer(dst, size); | |
| // Update the remaining number of bytes needed to read | |
| // and the destination buffer. | |
| size -= bytesCopied; | |
| SkASSERT(size + (fOffset - start) == totalSize); | |
| if (dst != nullptr) { | |
| dst += bytesCopied; | |
| } | |
| } | |
| // Buffer any more data that should be buffered, and copy it to the | |
| // destination. | |
| if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) { | |
| const size_t buffered = this->bufferAndWriteTo(dst, size); | |
| // Update the remaining number of bytes needed to read | |
| // and the destination buffer. | |
| size -= buffered; | |
| SkASSERT(size + (fOffset - start) == totalSize); | |
| if (dst != nullptr) { | |
| dst += buffered; | |
| } | |
| } | |
| if (size > 0 && !fStream->isAtEnd()) { | |
| SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size); | |
| SkDEBUGCODE(size -= bytesReadDirectly;) | |
| SkASSERT(size + (fOffset - start) == totalSize); | |
| } | |
| return fOffset - start; | |
| } | |
| } // anonymous namespace |