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