blob: 08364d15ebeaced04c755179c177ed221fee9a8d [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
mtklein36352bf2015-03-25 18:17:31 -070017 size_t read(void* buffer, size_t size) override;
scroggo@google.com09a53832013-11-12 20:53:05 +000018
scroggo028a4132015-04-02 13:19:51 -070019 bool peek(void* buffer, size_t size) const override;
20
mtklein36352bf2015-03-25 18:17:31 -070021 bool isAtEnd() const override;
scroggo@google.com09a53832013-11-12 20:53:05 +000022
mtklein36352bf2015-03-25 18:17:31 -070023 bool rewind() override;
scroggo@google.com09a53832013-11-12 20:53:05 +000024
mtklein36352bf2015-03-25 18:17:31 -070025 bool hasPosition() const override { return true; }
scroggo@google.com09a53832013-11-12 20:53:05 +000026
mtklein36352bf2015-03-25 18:17:31 -070027 size_t getPosition() const override { return fOffset; }
scroggo@google.com09a53832013-11-12 20:53:05 +000028
mtklein36352bf2015-03-25 18:17:31 -070029 bool hasLength() const override { return fHasLength; }
scroggo@google.com09a53832013-11-12 20:53:05 +000030
mtklein36352bf2015-03-25 18:17:31 -070031 size_t getLength() const override { return fLength; }
scroggo@google.com09a53832013-11-12 20:53:05 +000032
mtklein36352bf2015-03-25 18:17:31 -070033 SkStreamRewindable* duplicate() const override { return NULL; }
scroggo@google.com09a53832013-11-12 20:53:05 +000034
35private:
scroggoa1193e42015-01-21 12:09:53 -080036 SkAutoTDelete<SkStream> fStream;
commit-bot@chromium.org74b88b72014-02-10 22:03:21 +000037 const bool fHasLength;
38 const size_t fLength;
scroggo@google.com09a53832013-11-12 20:53:05 +000039 // Current offset into the stream. Always >= 0.
40 size_t fOffset;
41 // Amount that has been buffered by calls to read. Will always be less than
42 // fBufferSize.
43 size_t fBufferedSoFar;
44 // Total size of the buffer.
45 const size_t fBufferSize;
46 // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
47 // NULL stream.
48 SkAutoTMalloc<char> fBuffer;
49
50 // Read up to size bytes from already buffered data, and copy to
51 // dst, if non-NULL. Updates fOffset. Assumes that fOffset is less
52 // than fBufferedSoFar.
53 size_t readFromBuffer(char* dst, size_t size);
54
55 // Buffer up to size bytes from the stream, and copy to dst if non-
56 // NULL. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
57 // less than fBufferedSoFar, and size is greater than 0.
58 size_t bufferAndWriteTo(char* dst, size_t size);
59
60 // Read up to size bytes directly from the stream and into dst if non-
61 // NULL. Updates fOffset. Assumes fOffset is at or beyond the buffered
62 // data, and size is greater than 0.
63 size_t readDirectlyFromStream(char* dst, size_t size);
64
65 typedef SkStream INHERITED;
66};
scroggo@google.com83fd2c72013-09-26 21:35:39 +000067
68SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
69 if (NULL == stream) {
70 return NULL;
71 }
halcanary385fe4d2015-08-26 13:07:48 -070072 return new FrontBufferedStream(stream, bufferSize);
scroggo@google.com83fd2c72013-09-26 21:35:39 +000073}
74
scroggo@google.com09a53832013-11-12 20:53:05 +000075FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
scroggoa1193e42015-01-21 12:09:53 -080076 : fStream(stream)
commit-bot@chromium.org74b88b72014-02-10 22:03:21 +000077 , fHasLength(stream->hasPosition() && stream->hasLength())
78 , fLength(stream->getLength() - stream->getPosition())
scroggo@google.com83fd2c72013-09-26 21:35:39 +000079 , fOffset(0)
80 , fBufferedSoFar(0)
81 , fBufferSize(bufferSize)
82 , fBuffer(bufferSize) {}
83
scroggo@google.com09a53832013-11-12 20:53:05 +000084bool FrontBufferedStream::isAtEnd() const {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000085 if (fOffset < fBufferedSoFar) {
86 // Even if the underlying stream is at the end, this stream has been
87 // rewound after buffering, so it is not at the end.
88 return false;
89 }
90
91 return fStream->isAtEnd();
92}
93
scroggo@google.com09a53832013-11-12 20:53:05 +000094bool FrontBufferedStream::rewind() {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000095 // Only allow a rewind if we have not exceeded the buffer.
96 if (fOffset <= fBufferSize) {
97 fOffset = 0;
98 return true;
99 }
100 return false;
101}
102
scroggo@google.com09a53832013-11-12 20:53:05 +0000103size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000104 SkASSERT(fOffset < fBufferedSoFar);
105 // Some data has already been copied to fBuffer. Read up to the
106 // lesser of the size requested and the remainder of the buffered
107 // data.
108 const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
109 if (dst != NULL) {
110 memcpy(dst, fBuffer + fOffset, bytesToCopy);
111 }
112
113 // Update fOffset to the new position. It is guaranteed to be
114 // within the buffered data.
115 fOffset += bytesToCopy;
116 SkASSERT(fOffset <= fBufferedSoFar);
117
118 return bytesToCopy;
119}
120
scroggo@google.com09a53832013-11-12 20:53:05 +0000121size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000122 SkASSERT(size > 0);
123 SkASSERT(fOffset >= fBufferedSoFar);
mtklein1f66e452014-10-21 07:12:52 -0700124 SkASSERT(fBuffer);
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000125 // Data needs to be buffered. Buffer up to the lesser of the size requested
126 // and the remainder of the max buffer size.
127 const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
128 char* buffer = fBuffer + fOffset;
129 const size_t buffered = fStream->read(buffer, bytesToBuffer);
130
131 fBufferedSoFar += buffered;
132 fOffset = fBufferedSoFar;
133 SkASSERT(fBufferedSoFar <= fBufferSize);
134
135 // Copy the buffer to the destination buffer and update the amount read.
136 if (dst != NULL) {
137 memcpy(dst, buffer, buffered);
138 }
139
140 return buffered;
141}
142
scroggo@google.com09a53832013-11-12 20:53:05 +0000143size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000144 SkASSERT(size > 0);
145 // If we get here, we have buffered all that can be buffered.
146 SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
147
148 const size_t bytesReadDirectly = fStream->read(dst, size);
149 fOffset += bytesReadDirectly;
150
151 // If we have read past the end of the buffer, rewinding is no longer
152 // supported, so we can go ahead and free the memory.
153 if (bytesReadDirectly > 0) {
mtklein1f66e452014-10-21 07:12:52 -0700154 sk_free(fBuffer.detach());
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000155 }
156
157 return bytesReadDirectly;
158}
159
scroggo028a4132015-04-02 13:19:51 -0700160bool FrontBufferedStream::peek(void* dst, size_t size) const {
161 // Keep track of the offset so we can return to it.
162 const size_t start = fOffset;
163 if (start + size > fBufferSize) {
164 // This stream is not able to buffer enough.
165 return false;
166 }
167 FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
168 SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(dst, size);
169 SkASSERT(bytesRead == size);
170 nonConstThis->fOffset = start;
171 return true;
172}
173
scroggo@google.com09a53832013-11-12 20:53:05 +0000174size_t FrontBufferedStream::read(void* voidDst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000175 // Cast voidDst to a char* for easy addition.
176 char* dst = reinterpret_cast<char*>(voidDst);
177 SkDEBUGCODE(const size_t totalSize = size;)
178 const size_t start = fOffset;
179
180 // First, read any data that was previously buffered.
181 if (fOffset < fBufferedSoFar) {
182 const size_t bytesCopied = this->readFromBuffer(dst, size);
183
184 // Update the remaining number of bytes needed to read
185 // and the destination buffer.
186 size -= bytesCopied;
187 SkASSERT(size + (fOffset - start) == totalSize);
188 if (dst != NULL) {
189 dst += bytesCopied;
190 }
191 }
192
193 // Buffer any more data that should be buffered, and copy it to the
194 // destination.
scroggodd5a1e02014-10-21 08:06:05 -0700195 if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000196 const size_t buffered = this->bufferAndWriteTo(dst, size);
197
198 // Update the remaining number of bytes needed to read
199 // and the destination buffer.
200 size -= buffered;
201 SkASSERT(size + (fOffset - start) == totalSize);
202 if (dst != NULL) {
203 dst += buffered;
204 }
205 }
206
207 if (size > 0 && !fStream->isAtEnd()) {
scroggo@google.comfd67f2f2013-09-26 21:49:46 +0000208 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000209 SkDEBUGCODE(size -= bytesReadDirectly;)
210 SkASSERT(size + (fOffset - start) == totalSize);
211 }
212
213 return fOffset - start;
214}