1/* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/codec/SkCodec.h" 9#include "include/core/SkStream.h" 10#include "FrontBufferedStream.h" 11 12#include <algorithm> 13#include <memory> 14 15namespace { 16class FrontBufferedStream : public SkStreamRewindable { 17public: 18 // Called by Make. 19 FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize); 20 ~FrontBufferedStream() override; 21 22 bool failedToAllocateBuffer() const { return !fBuffer; } 23 24 size_t read(void* buffer, size_t size) override; 25 26 size_t peek(void* buffer, size_t size) const override; 27 28 bool isAtEnd() const override; 29 30 bool rewind() override; 31 32 bool hasLength() const override { return fHasLength; } 33 34 size_t getLength() const override { return fLength; } 35 36private: 37 SkStreamRewindable* onDuplicate() const override { return nullptr; } 38 39 std::unique_ptr<SkStream> fStream; 40 const bool fHasLength; 41 const size_t fLength; 42 // Current offset into the stream. Always >= 0. 43 size_t fOffset; 44 // Amount that has been buffered by calls to read. Will always be less than 45 // fBufferSize. 46 size_t fBufferedSoFar; 47 // Total size of the buffer. 48 const size_t fBufferSize; 49 char* fBuffer; 50 inline static constexpr size_t kStorageSize = SkCodec::MinBufferedBytesNeeded(); 51 char fStorage[kStorageSize]; 52 53 // Read up to size bytes from already buffered data, and copy to 54 // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less 55 // than fBufferedSoFar. 56 size_t readFromBuffer(char* dst, size_t size); 57 58 // Buffer up to size bytes from the stream, and copy to dst if non- 59 // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is 60 // less than fBufferedSoFar, and size is greater than 0. 61 size_t bufferAndWriteTo(char* dst, size_t size); 62 63 // Read up to size bytes directly from the stream and into dst if non- 64 // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered 65 // data, and size is greater than 0. 66 size_t readDirectlyFromStream(char* dst, size_t size); 67 68 using INHERITED = SkStream; 69}; 70} // anonymous namespace 71 72namespace android { 73namespace skia { 74 75std::unique_ptr<SkStreamRewindable> FrontBufferedStream::Make(std::unique_ptr<SkStream> stream, 76 size_t bufferSize) { 77 if (!stream) { 78 return nullptr; 79 } 80 auto frontBufferedStream = std::make_unique<::FrontBufferedStream>( 81 std::move(stream), bufferSize); 82 if (frontBufferedStream->failedToAllocateBuffer()) { 83 return nullptr; 84 } 85 86 // Work around a warning regarding a copy on older compilers. 87 return std::move(frontBufferedStream); 88} 89} // namespace skia 90} // namespace android 91 92namespace { 93FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize) 94 : fStream(std::move(stream)) 95 , fHasLength(fStream->hasPosition() && fStream->hasLength()) 96 , fLength(fStream->getLength() - fStream->getPosition()) 97 , fOffset(0) 98 , fBufferedSoFar(0) 99 , fBufferSize(bufferSize) 100 , fBuffer(bufferSize <= kStorageSize ? fStorage 101 : reinterpret_cast<char*>(malloc(bufferSize))) {} 102 103FrontBufferedStream::~FrontBufferedStream() { 104 if (fBuffer != fStorage) { 105 free(fBuffer); 106 } 107} 108 109bool FrontBufferedStream::isAtEnd() const { 110 if (fOffset < fBufferedSoFar) { 111 // Even if the underlying stream is at the end, this stream has been 112 // rewound after buffering, so it is not at the end. 113 return false; 114 } 115 116 return fStream->isAtEnd(); 117} 118 119bool FrontBufferedStream::rewind() { 120 // Only allow a rewind if we have not exceeded the buffer. 121 if (fOffset <= fBufferSize) { 122 fOffset = 0; 123 return true; 124 } 125 return false; 126} 127 128size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) { 129 SkASSERT(fOffset < fBufferedSoFar); 130 // Some data has already been copied to fBuffer. Read up to the 131 // lesser of the size requested and the remainder of the buffered 132 // data. 133 const size_t bytesToCopy = std::min(size, fBufferedSoFar - fOffset); 134 if (dst != nullptr) { 135 memcpy(dst, fBuffer + fOffset, bytesToCopy); 136 } 137 138 // Update fOffset to the new position. It is guaranteed to be 139 // within the buffered data. 140 fOffset += bytesToCopy; 141 SkASSERT(fOffset <= fBufferedSoFar); 142 143 return bytesToCopy; 144} 145 146size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) { 147 SkASSERT(size > 0); 148 SkASSERT(fOffset >= fBufferedSoFar); 149 SkASSERT(fBuffer); 150 // Data needs to be buffered. Buffer up to the lesser of the size requested 151 // and the remainder of the max buffer size. 152 const size_t bytesToBuffer = std::min(size, fBufferSize - fBufferedSoFar); 153 char* buffer = fBuffer + fOffset; 154 const size_t buffered = fStream->read(buffer, bytesToBuffer); 155 156 fBufferedSoFar += buffered; 157 fOffset = fBufferedSoFar; 158 SkASSERT(fBufferedSoFar <= fBufferSize); 159 160 // Copy the buffer to the destination buffer and update the amount read. 161 if (dst != nullptr) { 162 memcpy(dst, buffer, buffered); 163 } 164 165 return buffered; 166} 167 168size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) { 169 SkASSERT(size > 0); 170 // If we get here, we have buffered all that can be buffered. 171 SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize); 172 173 const size_t bytesReadDirectly = fStream->read(dst, size); 174 fOffset += bytesReadDirectly; 175 176 // If we have read past the end of the buffer, rewinding is no longer 177 // supported, so we can go ahead and free the memory. 178 if (bytesReadDirectly > 0 && fBuffer != fStorage) { 179 free(fBuffer); 180 fBuffer = nullptr; 181 } 182 183 return bytesReadDirectly; 184} 185 186size_t FrontBufferedStream::peek(void* dst, size_t size) const { 187 // Keep track of the offset so we can return to it. 188 const size_t start = fOffset; 189 190 if (start >= fBufferSize) { 191 // This stream is not able to buffer. 192 return 0; 193 } 194 195 size = std::min(size, fBufferSize - start); 196 FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this); 197 const size_t bytesRead = nonConstThis->read(dst, size); 198 nonConstThis->fOffset = start; 199 return bytesRead; 200} 201 202size_t FrontBufferedStream::read(void* voidDst, size_t size) { 203 // Cast voidDst to a char* for easy addition. 204 char* dst = reinterpret_cast<char*>(voidDst); 205 SkDEBUGCODE(const size_t totalSize = size;) 206 const size_t start = fOffset; 207 208 // First, read any data that was previously buffered. 209 if (fOffset < fBufferedSoFar) { 210 const size_t bytesCopied = this->readFromBuffer(dst, size); 211 212 // Update the remaining number of bytes needed to read 213 // and the destination buffer. 214 size -= bytesCopied; 215 SkASSERT(size + (fOffset - start) == totalSize); 216 if (dst != nullptr) { 217 dst += bytesCopied; 218 } 219 } 220 221 // Buffer any more data that should be buffered, and copy it to the 222 // destination. 223 if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) { 224 const size_t buffered = this->bufferAndWriteTo(dst, size); 225 226 // Update the remaining number of bytes needed to read 227 // and the destination buffer. 228 size -= buffered; 229 SkASSERT(size + (fOffset - start) == totalSize); 230 if (dst != nullptr) { 231 dst += buffered; 232 } 233 } 234 235 if (size > 0 && !fStream->isAtEnd()) { 236 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size); 237 SkDEBUGCODE(size -= bytesReadDirectly;) 238 SkASSERT(size + (fOffset - start) == totalSize); 239 } 240 241 return fOffset - start; 242} 243} // anonymous namespace 244