xref: /aosp_15_r20/external/skia/client_utils/android/FrontBufferedStream.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/codec/SkCodec.h"
9 #include "include/core/SkStream.h"
10 #include "FrontBufferedStream.h"
11 
12 #include <algorithm>
13 #include <memory>
14 
15 namespace {
16 class FrontBufferedStream : public SkStreamRewindable {
17 public:
18     // Called by Make.
19     FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize);
20     ~FrontBufferedStream() override;
21 
failedToAllocateBuffer() const22     bool failedToAllocateBuffer() const { return !fBuffer; }
23 
24     size_t read(void* buffer, size_t size) override;
25 
26     size_t peek(void* buffer, size_t size) const override;
27 
28     bool isAtEnd() const override;
29 
30     bool rewind() override;
31 
hasLength() const32     bool hasLength() const override { return fHasLength; }
33 
getLength() const34     size_t getLength() const override { return fLength; }
35 
36 private:
onDuplicate() const37     SkStreamRewindable* onDuplicate() const override { return nullptr; }
38 
39     std::unique_ptr<SkStream>        fStream;
40     const bool                       fHasLength;
41     const size_t                     fLength;
42     // Current offset into the stream. Always >= 0.
43     size_t                           fOffset;
44     // Amount that has been buffered by calls to read. Will always be less than
45     // fBufferSize.
46     size_t                           fBufferedSoFar;
47     // Total size of the buffer.
48     const size_t                     fBufferSize;
49     char*                            fBuffer;
50     inline static constexpr size_t   kStorageSize = SkCodec::MinBufferedBytesNeeded();
51     char                             fStorage[kStorageSize];
52 
53     // Read up to size bytes from already buffered data, and copy to
54     // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less
55     // than fBufferedSoFar.
56     size_t readFromBuffer(char* dst, size_t size);
57 
58     // Buffer up to size bytes from the stream, and copy to dst if non-
59     // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
60     // less than fBufferedSoFar, and size is greater than 0.
61     size_t bufferAndWriteTo(char* dst, size_t size);
62 
63     // Read up to size bytes directly from the stream and into dst if non-
64     // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered
65     // data, and size is greater than 0.
66     size_t readDirectlyFromStream(char* dst, size_t size);
67 
68     using INHERITED = SkStream;
69 };
70 } // anonymous namespace
71 
72 namespace android {
73 namespace skia {
74 
Make(std::unique_ptr<SkStream> stream,size_t bufferSize)75 std::unique_ptr<SkStreamRewindable> FrontBufferedStream::Make(std::unique_ptr<SkStream> stream,
76                                                               size_t bufferSize) {
77     if (!stream) {
78         return nullptr;
79     }
80     auto frontBufferedStream = std::make_unique<::FrontBufferedStream>(
81             std::move(stream), bufferSize);
82     if (frontBufferedStream->failedToAllocateBuffer()) {
83         return nullptr;
84     }
85 
86     return frontBufferedStream;
87 }
88 } // namespace skia
89 } // namespace android
90 
91 namespace {
FrontBufferedStream(std::unique_ptr<SkStream> stream,size_t bufferSize)92 FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
93     : fStream(std::move(stream))
94     , fHasLength(fStream->hasPosition() && fStream->hasLength())
95     , fLength(fStream->getLength() - fStream->getPosition())
96     , fOffset(0)
97     , fBufferedSoFar(0)
98     , fBufferSize(bufferSize)
99     , fBuffer(bufferSize <= kStorageSize ? fStorage
100                                          : reinterpret_cast<char*>(malloc(bufferSize))) {}
101 
~FrontBufferedStream()102 FrontBufferedStream::~FrontBufferedStream() {
103     if (fBuffer != fStorage) {
104         free(fBuffer);
105     }
106 }
107 
isAtEnd() const108 bool FrontBufferedStream::isAtEnd() const {
109     if (fOffset < fBufferedSoFar) {
110         // Even if the underlying stream is at the end, this stream has been
111         // rewound after buffering, so it is not at the end.
112         return false;
113     }
114 
115     return fStream->isAtEnd();
116 }
117 
rewind()118 bool FrontBufferedStream::rewind() {
119     // Only allow a rewind if we have not exceeded the buffer.
120     if (fOffset <= fBufferSize) {
121         fOffset = 0;
122         return true;
123     }
124     return false;
125 }
126 
readFromBuffer(char * dst,size_t size)127 size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
128     SkASSERT(fOffset < fBufferedSoFar);
129     // Some data has already been copied to fBuffer. Read up to the
130     // lesser of the size requested and the remainder of the buffered
131     // data.
132     const size_t bytesToCopy = std::min(size, fBufferedSoFar - fOffset);
133     if (dst != nullptr) {
134         memcpy(dst, fBuffer + fOffset, bytesToCopy);
135     }
136 
137     // Update fOffset to the new position. It is guaranteed to be
138     // within the buffered data.
139     fOffset += bytesToCopy;
140     SkASSERT(fOffset <= fBufferedSoFar);
141 
142     return bytesToCopy;
143 }
144 
bufferAndWriteTo(char * dst,size_t size)145 size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
146     SkASSERT(size > 0);
147     SkASSERT(fOffset >= fBufferedSoFar);
148     SkASSERT(fBuffer);
149     // Data needs to be buffered. Buffer up to the lesser of the size requested
150     // and the remainder of the max buffer size.
151     const size_t bytesToBuffer = std::min(size, fBufferSize - fBufferedSoFar);
152     char* buffer = fBuffer + fOffset;
153     const size_t buffered = fStream->read(buffer, bytesToBuffer);
154 
155     fBufferedSoFar += buffered;
156     fOffset = fBufferedSoFar;
157     SkASSERT(fBufferedSoFar <= fBufferSize);
158 
159     // Copy the buffer to the destination buffer and update the amount read.
160     if (dst != nullptr) {
161         memcpy(dst, buffer, buffered);
162     }
163 
164     return buffered;
165 }
166 
readDirectlyFromStream(char * dst,size_t size)167 size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
168     SkASSERT(size > 0);
169     // If we get here, we have buffered all that can be buffered.
170     SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
171 
172     const size_t bytesReadDirectly = fStream->read(dst, size);
173     fOffset += bytesReadDirectly;
174 
175     // If we have read past the end of the buffer, rewinding is no longer
176     // supported, so we can go ahead and free the memory.
177     if (bytesReadDirectly > 0 && fBuffer != fStorage) {
178         free(fBuffer);
179         fBuffer = nullptr;
180     }
181 
182     return bytesReadDirectly;
183 }
184 
peek(void * dst,size_t size) const185 size_t FrontBufferedStream::peek(void* dst, size_t size) const {
186     // Keep track of the offset so we can return to it.
187     const size_t start = fOffset;
188 
189     if (start >= fBufferSize) {
190         // This stream is not able to buffer.
191         return 0;
192     }
193 
194     size = std::min(size, fBufferSize - start);
195     FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
196     const size_t bytesRead = nonConstThis->read(dst, size);
197     nonConstThis->fOffset = start;
198     return bytesRead;
199 }
200 
read(void * voidDst,size_t size)201 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
202     // Cast voidDst to a char* for easy addition.
203     char* dst = reinterpret_cast<char*>(voidDst);
204     SkDEBUGCODE(const size_t totalSize = size;)
205     const size_t start = fOffset;
206 
207     // First, read any data that was previously buffered.
208     if (fOffset < fBufferedSoFar) {
209         const size_t bytesCopied = this->readFromBuffer(dst, size);
210 
211         // Update the remaining number of bytes needed to read
212         // and the destination buffer.
213         size -= bytesCopied;
214         SkASSERT(size + (fOffset - start) == totalSize);
215         if (dst != nullptr) {
216             dst += bytesCopied;
217         }
218     }
219 
220     // Buffer any more data that should be buffered, and copy it to the
221     // destination.
222     if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
223         const size_t buffered = this->bufferAndWriteTo(dst, size);
224 
225         // Update the remaining number of bytes needed to read
226         // and the destination buffer.
227         size -= buffered;
228         SkASSERT(size + (fOffset - start) == totalSize);
229         if (dst != nullptr) {
230             dst += buffered;
231         }
232     }
233 
234     if (size > 0 && !fStream->isAtEnd()) {
235         SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
236         SkDEBUGCODE(size -= bytesReadDirectly;)
237         SkASSERT(size + (fOffset - start) == totalSize);
238     }
239 
240     return fOffset - start;
241 }
242 } // anonymous namespace
243