blob: 8cb3931082c75d268e793212a73f688a1dbe227f [file] [log] [blame]
scroggo@google.com83fd2c72013-09-26 21:35:39 +00001/*
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.com09a53832013-11-12 20:53:05 +00009#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 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.org74b88b72014-02-10 22:03:21 +000027 virtual bool hasLength() const SK_OVERRIDE { return fHasLength; }
scroggo@google.com09a53832013-11-12 20:53:05 +000028
commit-bot@chromium.org74b88b72014-02-10 22:03:21 +000029 virtual size_t getLength() const SK_OVERRIDE { return fLength; }
scroggo@google.com09a53832013-11-12 20:53:05 +000030
31 virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
32
33private:
34 SkAutoTUnref<SkStream> fStream;
commit-bot@chromium.org74b88b72014-02-10 22:03:21 +000035 const bool fHasLength;
36 const size_t fLength;
scroggo@google.com09a53832013-11-12 20:53:05 +000037 // 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.com83fd2c72013-09-26 21:35:39 +000065
66SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
67 if (NULL == stream) {
68 return NULL;
69 }
scroggo@google.com09a53832013-11-12 20:53:05 +000070 return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize));
scroggo@google.com83fd2c72013-09-26 21:35:39 +000071}
72
scroggo@google.com09a53832013-11-12 20:53:05 +000073FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
scroggo@google.com83fd2c72013-09-26 21:35:39 +000074 : fStream(SkRef(stream))
commit-bot@chromium.org74b88b72014-02-10 22:03:21 +000075 , fHasLength(stream->hasPosition() && stream->hasLength())
76 , fLength(stream->getLength() - stream->getPosition())
scroggo@google.com83fd2c72013-09-26 21:35:39 +000077 , fOffset(0)
78 , fBufferedSoFar(0)
79 , fBufferSize(bufferSize)
80 , fBuffer(bufferSize) {}
81
scroggo@google.com09a53832013-11-12 20:53:05 +000082bool FrontBufferedStream::isAtEnd() const {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000083 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.com09a53832013-11-12 20:53:05 +000092bool FrontBufferedStream::rewind() {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000093 // 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.com09a53832013-11-12 20:53:05 +0000101size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000102 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.com09a53832013-11-12 20:53:05 +0000119size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000120 SkASSERT(size > 0);
121 SkASSERT(fOffset >= fBufferedSoFar);
122 // Data needs to be buffered. Buffer up to the lesser of the size requested
123 // and the remainder of the max buffer size.
124 const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
125 char* buffer = fBuffer + fOffset;
126 const size_t buffered = fStream->read(buffer, bytesToBuffer);
127
128 fBufferedSoFar += buffered;
129 fOffset = fBufferedSoFar;
130 SkASSERT(fBufferedSoFar <= fBufferSize);
131
132 // Copy the buffer to the destination buffer and update the amount read.
133 if (dst != NULL) {
134 memcpy(dst, buffer, buffered);
135 }
136
137 return buffered;
138}
139
scroggo@google.com09a53832013-11-12 20:53:05 +0000140size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000141 SkASSERT(size > 0);
142 // If we get here, we have buffered all that can be buffered.
143 SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
144
145 const size_t bytesReadDirectly = fStream->read(dst, size);
146 fOffset += bytesReadDirectly;
147
148 // If we have read past the end of the buffer, rewinding is no longer
149 // supported, so we can go ahead and free the memory.
150 if (bytesReadDirectly > 0) {
151 fBuffer.reset(0);
152 }
153
154 return bytesReadDirectly;
155}
156
scroggo@google.com09a53832013-11-12 20:53:05 +0000157size_t FrontBufferedStream::read(void* voidDst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000158 // Cast voidDst to a char* for easy addition.
159 char* dst = reinterpret_cast<char*>(voidDst);
160 SkDEBUGCODE(const size_t totalSize = size;)
161 const size_t start = fOffset;
162
163 // First, read any data that was previously buffered.
164 if (fOffset < fBufferedSoFar) {
165 const size_t bytesCopied = this->readFromBuffer(dst, size);
166
167 // Update the remaining number of bytes needed to read
168 // and the destination buffer.
169 size -= bytesCopied;
170 SkASSERT(size + (fOffset - start) == totalSize);
171 if (dst != NULL) {
172 dst += bytesCopied;
173 }
174 }
175
176 // Buffer any more data that should be buffered, and copy it to the
177 // destination.
178 if (size > 0 && fBufferedSoFar < fBufferSize) {
179 const size_t buffered = this->bufferAndWriteTo(dst, size);
180
181 // Update the remaining number of bytes needed to read
182 // and the destination buffer.
183 size -= buffered;
184 SkASSERT(size + (fOffset - start) == totalSize);
185 if (dst != NULL) {
186 dst += buffered;
187 }
188 }
189
190 if (size > 0 && !fStream->isAtEnd()) {
scroggo@google.comfd67f2f2013-09-26 21:49:46 +0000191 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000192 SkDEBUGCODE(size -= bytesReadDirectly;)
193 SkASSERT(size + (fOffset - start) == totalSize);
194 }
195
196 return fOffset - start;
197}