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