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 | |
25 | bool hasLength() const override { return fHasLength; } |
26 | |
27 | size_t getLength() const override { return fLength; } |
28 | |
29 | private: |
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 | |
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 | |
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 | |
82 | 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 | |
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 | |
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 = 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 | |
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 = 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 | |
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 | |
158 | 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 = 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 | |
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 | |