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 "include/core/SkStream.h"
9#include "include/private/SkTemplates.h"
10#include "include/utils/SkFrontBufferedStream.h"
11
12class FrontBufferedStream : public SkStreamRewindable {
13public:
14 // Called by Make.
15 FrontBufferedStream(std::unique_ptr<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
29private:
30 SkStreamRewindable* onDuplicate() const override { return nullptr; }
31
32 std::unique_ptr<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
64std::unique_ptr<SkStreamRewindable> SkFrontBufferedStream::Make(std::unique_ptr<SkStream> stream,
65 size_t bufferSize) {
66 if (!stream) {
67 return nullptr;
68 }
69 return std::unique_ptr<SkStreamRewindable>(new FrontBufferedStream(std::move(stream),
70 bufferSize));
71}
72
73FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
74 : fStream(std::move(stream))
75 , fHasLength(fStream->hasPosition() && fStream->hasLength())
76 , fLength(fStream->getLength() - fStream->getPosition())
77 , fOffset(0)
78 , fBufferedSoFar(0)
79 , fBufferSize(bufferSize)
80 , fBuffer(bufferSize) {}
81
82bool FrontBufferedStream::isAtEnd() const {
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
92bool FrontBufferedStream::rewind() {
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
101size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
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 = std::min(size, fBufferedSoFar - fOffset);
107 if (dst != nullptr) {
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
119size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
120 SkASSERT(size > 0);
121 SkASSERT(fOffset >= fBufferedSoFar);
122 SkASSERT(fBuffer);
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 = std::min(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 != nullptr) {
135 memcpy(dst, buffer, buffered);
136 }
137
138 return buffered;
139}
140
141size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
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) {
152 sk_free(fBuffer.release());
153 }
154
155 return bytesReadDirectly;
156}
157
158size_t FrontBufferedStream::peek(void* dst, size_t size) const {
159 // Keep track of the offset so we can return to it.
160 const size_t start = fOffset;
161
162 if (start >= fBufferSize) {
163 // This stream is not able to buffer.
164 return 0;
165 }
166
167 size = std::min(size, fBufferSize - start);
168 FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
169 const size_t bytesRead = nonConstThis->read(dst, size);
170 nonConstThis->fOffset = start;
171 return bytesRead;
172}
173
174size_t FrontBufferedStream::read(void* voidDst, size_t size) {
175 // 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 != nullptr) {
189 dst += bytesCopied;
190 }
191 }
192
193 // Buffer any more data that should be buffered, and copy it to the
194 // destination.
195 if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
196 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 != nullptr) {
203 dst += buffered;
204 }
205 }
206
207 if (size > 0 && !fStream->isAtEnd()) {
208 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
209 SkDEBUGCODE(size -= bytesReadDirectly;)
210 SkASSERT(size + (fOffset - start) == totalSize);
211 }
212
213 return fOffset - start;
214}
215