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 "SkFrontBufferedStream.h" 9#include "SkStream.h" 10#include "SkTemplates.h" 11 12class FrontBufferedStream : public SkStreamRewindable { 13public: 14 // Called by Create. 15 FrontBufferedStream(SkStream*, size_t bufferSize); 16 17 size_t read(void* buffer, size_t size) override; 18 19 size_t peek(void* buffer, size_t size) const override; 20 21 bool isAtEnd() const override; 22 23 bool rewind() override; 24 25 bool hasLength() const override { return fHasLength; } 26 27 size_t getLength() const override { return fLength; } 28 29 SkStreamRewindable* duplicate() const override { return nullptr; } 30 31private: 32 SkAutoTDelete<SkStream> fStream; 33 const bool fHasLength; 34 const size_t fLength; 35 // Current offset into the stream. Always >= 0. 36 size_t fOffset; 37 // Amount that has been buffered by calls to read. Will always be less than 38 // fBufferSize. 39 size_t fBufferedSoFar; 40 // Total size of the buffer. 41 const size_t fBufferSize; 42 // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a 43 // nullptr stream. 44 SkAutoTMalloc<char> fBuffer; 45 46 // Read up to size bytes from already buffered data, and copy to 47 // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less 48 // than fBufferedSoFar. 49 size_t readFromBuffer(char* dst, size_t size); 50 51 // Buffer up to size bytes from the stream, and copy to dst if non- 52 // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is 53 // less than fBufferedSoFar, and size is greater than 0. 54 size_t bufferAndWriteTo(char* dst, size_t size); 55 56 // Read up to size bytes directly from the stream and into dst if non- 57 // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered 58 // data, and size is greater than 0. 59 size_t readDirectlyFromStream(char* dst, size_t size); 60 61 typedef SkStream INHERITED; 62}; 63 64SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) { 65 if (nullptr == stream) { 66 return nullptr; 67 } 68 return new FrontBufferedStream(stream, bufferSize); 69} 70 71FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize) 72 : fStream(stream) 73 , fHasLength(stream->hasPosition() && stream->hasLength()) 74 , fLength(stream->getLength() - stream->getPosition()) 75 , fOffset(0) 76 , fBufferedSoFar(0) 77 , fBufferSize(bufferSize) 78 , fBuffer(bufferSize) {} 79 80bool FrontBufferedStream::isAtEnd() const { 81 if (fOffset < fBufferedSoFar) { 82 // Even if the underlying stream is at the end, this stream has been 83 // rewound after buffering, so it is not at the end. 84 return false; 85 } 86 87 return fStream->isAtEnd(); 88} 89 90bool FrontBufferedStream::rewind() { 91 // Only allow a rewind if we have not exceeded the buffer. 92 if (fOffset <= fBufferSize) { 93 fOffset = 0; 94 return true; 95 } 96 return false; 97} 98 99size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) { 100 SkASSERT(fOffset < fBufferedSoFar); 101 // Some data has already been copied to fBuffer. Read up to the 102 // lesser of the size requested and the remainder of the buffered 103 // data. 104 const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset); 105 if (dst != nullptr) { 106 memcpy(dst, fBuffer + fOffset, bytesToCopy); 107 } 108 109 // Update fOffset to the new position. It is guaranteed to be 110 // within the buffered data. 111 fOffset += bytesToCopy; 112 SkASSERT(fOffset <= fBufferedSoFar); 113 114 return bytesToCopy; 115} 116 117size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) { 118 SkASSERT(size > 0); 119 SkASSERT(fOffset >= fBufferedSoFar); 120 SkASSERT(fBuffer); 121 // Data needs to be buffered. Buffer up to the lesser of the size requested 122 // and the remainder of the max buffer size. 123 const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar); 124 char* buffer = fBuffer + fOffset; 125 const size_t buffered = fStream->read(buffer, bytesToBuffer); 126 127 fBufferedSoFar += buffered; 128 fOffset = fBufferedSoFar; 129 SkASSERT(fBufferedSoFar <= fBufferSize); 130 131 // Copy the buffer to the destination buffer and update the amount read. 132 if (dst != nullptr) { 133 memcpy(dst, buffer, buffered); 134 } 135 136 return buffered; 137} 138 139size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) { 140 SkASSERT(size > 0); 141 // If we get here, we have buffered all that can be buffered. 142 SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize); 143 144 const size_t bytesReadDirectly = fStream->read(dst, size); 145 fOffset += bytesReadDirectly; 146 147 // If we have read past the end of the buffer, rewinding is no longer 148 // supported, so we can go ahead and free the memory. 149 if (bytesReadDirectly > 0) { 150 sk_free(fBuffer.detach()); 151 } 152 153 return bytesReadDirectly; 154} 155 156size_t FrontBufferedStream::peek(void* dst, size_t size) const { 157 // Keep track of the offset so we can return to it. 158 const size_t start = fOffset; 159 160 if (start >= fBufferSize) { 161 // This stream is not able to buffer. 162 return 0; 163 } 164 165 size = SkTMin(size, fBufferSize - start); 166 FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this); 167 const size_t bytesRead = nonConstThis->read(dst, size); 168 nonConstThis->fOffset = start; 169 return bytesRead; 170} 171 172size_t FrontBufferedStream::read(void* voidDst, size_t size) { 173 // Cast voidDst to a char* for easy addition. 174 char* dst = reinterpret_cast<char*>(voidDst); 175 SkDEBUGCODE(const size_t totalSize = size;) 176 const size_t start = fOffset; 177 178 // First, read any data that was previously buffered. 179 if (fOffset < fBufferedSoFar) { 180 const size_t bytesCopied = this->readFromBuffer(dst, size); 181 182 // Update the remaining number of bytes needed to read 183 // and the destination buffer. 184 size -= bytesCopied; 185 SkASSERT(size + (fOffset - start) == totalSize); 186 if (dst != nullptr) { 187 dst += bytesCopied; 188 } 189 } 190 191 // Buffer any more data that should be buffered, and copy it to the 192 // destination. 193 if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) { 194 const size_t buffered = this->bufferAndWriteTo(dst, size); 195 196 // Update the remaining number of bytes needed to read 197 // and the destination buffer. 198 size -= buffered; 199 SkASSERT(size + (fOffset - start) == totalSize); 200 if (dst != nullptr) { 201 dst += buffered; 202 } 203 } 204 205 if (size > 0 && !fStream->isAtEnd()) { 206 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size); 207 SkDEBUGCODE(size -= bytesReadDirectly;) 208 SkASSERT(size + (fOffset - start) == totalSize); 209 } 210 211 return fOffset - start; 212} 213