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
15namespace {
16class FrontBufferedStream : public SkStreamRewindable {
17public:
18    // Called by Make.
19    FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize);
20    ~FrontBufferedStream() override;
21
22    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
32    bool hasLength() const override { return fHasLength; }
33
34    size_t getLength() const override { return fLength; }
35
36private:
37    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
72namespace android {
73namespace skia {
74
75std::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    // Work around a warning regarding a copy on older compilers.
87    return std::move(frontBufferedStream);
88}
89} // namespace skia
90} // namespace android
91
92namespace {
93FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
94    : fStream(std::move(stream))
95    , fHasLength(fStream->hasPosition() && fStream->hasLength())
96    , fLength(fStream->getLength() - fStream->getPosition())
97    , fOffset(0)
98    , fBufferedSoFar(0)
99    , fBufferSize(bufferSize)
100    , fBuffer(bufferSize <= kStorageSize ? fStorage
101                                         : reinterpret_cast<char*>(malloc(bufferSize))) {}
102
103FrontBufferedStream::~FrontBufferedStream() {
104    if (fBuffer != fStorage) {
105        free(fBuffer);
106    }
107}
108
109bool FrontBufferedStream::isAtEnd() const {
110    if (fOffset < fBufferedSoFar) {
111        // Even if the underlying stream is at the end, this stream has been
112        // rewound after buffering, so it is not at the end.
113        return false;
114    }
115
116    return fStream->isAtEnd();
117}
118
119bool FrontBufferedStream::rewind() {
120    // Only allow a rewind if we have not exceeded the buffer.
121    if (fOffset <= fBufferSize) {
122        fOffset = 0;
123        return true;
124    }
125    return false;
126}
127
128size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
129    SkASSERT(fOffset < fBufferedSoFar);
130    // Some data has already been copied to fBuffer. Read up to the
131    // lesser of the size requested and the remainder of the buffered
132    // data.
133    const size_t bytesToCopy = std::min(size, fBufferedSoFar - fOffset);
134    if (dst != nullptr) {
135        memcpy(dst, fBuffer + fOffset, bytesToCopy);
136    }
137
138    // Update fOffset to the new position. It is guaranteed to be
139    // within the buffered data.
140    fOffset += bytesToCopy;
141    SkASSERT(fOffset <= fBufferedSoFar);
142
143    return bytesToCopy;
144}
145
146size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
147    SkASSERT(size > 0);
148    SkASSERT(fOffset >= fBufferedSoFar);
149    SkASSERT(fBuffer);
150    // Data needs to be buffered. Buffer up to the lesser of the size requested
151    // and the remainder of the max buffer size.
152    const size_t bytesToBuffer = std::min(size, fBufferSize - fBufferedSoFar);
153    char* buffer = fBuffer + fOffset;
154    const size_t buffered = fStream->read(buffer, bytesToBuffer);
155
156    fBufferedSoFar += buffered;
157    fOffset = fBufferedSoFar;
158    SkASSERT(fBufferedSoFar <= fBufferSize);
159
160    // Copy the buffer to the destination buffer and update the amount read.
161    if (dst != nullptr) {
162        memcpy(dst, buffer, buffered);
163    }
164
165    return buffered;
166}
167
168size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
169    SkASSERT(size > 0);
170    // If we get here, we have buffered all that can be buffered.
171    SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
172
173    const size_t bytesReadDirectly = fStream->read(dst, size);
174    fOffset += bytesReadDirectly;
175
176    // If we have read past the end of the buffer, rewinding is no longer
177    // supported, so we can go ahead and free the memory.
178    if (bytesReadDirectly > 0 && fBuffer != fStorage) {
179        free(fBuffer);
180        fBuffer = nullptr;
181    }
182
183    return bytesReadDirectly;
184}
185
186size_t FrontBufferedStream::peek(void* dst, size_t size) const {
187    // Keep track of the offset so we can return to it.
188    const size_t start = fOffset;
189
190    if (start >= fBufferSize) {
191        // This stream is not able to buffer.
192        return 0;
193    }
194
195    size = std::min(size, fBufferSize - start);
196    FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
197    const size_t bytesRead = nonConstThis->read(dst, size);
198    nonConstThis->fOffset = start;
199    return bytesRead;
200}
201
202size_t FrontBufferedStream::read(void* voidDst, size_t size) {
203    // Cast voidDst to a char* for easy addition.
204    char* dst = reinterpret_cast<char*>(voidDst);
205    SkDEBUGCODE(const size_t totalSize = size;)
206    const size_t start = fOffset;
207
208    // First, read any data that was previously buffered.
209    if (fOffset < fBufferedSoFar) {
210        const size_t bytesCopied = this->readFromBuffer(dst, size);
211
212        // Update the remaining number of bytes needed to read
213        // and the destination buffer.
214        size -= bytesCopied;
215        SkASSERT(size + (fOffset - start) == totalSize);
216        if (dst != nullptr) {
217            dst += bytesCopied;
218        }
219    }
220
221    // Buffer any more data that should be buffered, and copy it to the
222    // destination.
223    if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
224        const size_t buffered = this->bufferAndWriteTo(dst, size);
225
226        // Update the remaining number of bytes needed to read
227        // and the destination buffer.
228        size -= buffered;
229        SkASSERT(size + (fOffset - start) == totalSize);
230        if (dst != nullptr) {
231            dst += buffered;
232        }
233    }
234
235    if (size > 0 && !fStream->isAtEnd()) {
236        SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
237        SkDEBUGCODE(size -= bytesReadDirectly;)
238        SkASSERT(size + (fOffset - start) == totalSize);
239    }
240
241    return fOffset - start;
242}
243} // anonymous namespace
244