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 
12 class FrontBufferedStream : public SkStreamRewindable {
13 public:
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 
hasLength() const25     bool hasLength() const override { return fHasLength; }
26 
getLength() const27     size_t getLength() const override { return fLength; }
28 
29 private:
onDuplicate() const30     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 
Make(std::unique_ptr<SkStream> stream,size_t bufferSize)64 std::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 
FrontBufferedStream(std::unique_ptr<SkStream> stream,size_t bufferSize)73 FrontBufferedStream::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 
isAtEnd() const82 bool 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 
rewind()92 bool 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 
readFromBuffer(char * dst,size_t size)101 size_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 = SkTMin(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 
bufferAndWriteTo(char * dst,size_t size)119 size_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 = 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 != nullptr) {
135         memcpy(dst, buffer, buffered);
136     }
137 
138     return buffered;
139 }
140 
readDirectlyFromStream(char * dst,size_t size)141 size_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 
peek(void * dst,size_t size) const158 size_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 = SkTMin(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 
read(void * voidDst,size_t size)174 size_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