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 "SkFrontBufferedStream.h"
9#include "SkStream.h"
10#include "SkTemplates.h"
11
12class FrontBufferedStream : public SkStreamRewindable {
13public:
14    // Called by Create.
15    FrontBufferedStream(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    SkStreamRewindable* duplicate() const override { return nullptr; }
30
31private:
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
64SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
65    if (nullptr == stream) {
66        return nullptr;
67    }
68    return new FrontBufferedStream(stream, bufferSize);
69}
70
71FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
72    : fStream(stream)
73    , fHasLength(stream->hasPosition() && stream->hasLength())
74    , fLength(stream->getLength() - stream->getPosition())
75    , fOffset(0)
76    , fBufferedSoFar(0)
77    , fBufferSize(bufferSize)
78    , fBuffer(bufferSize) {}
79
80bool FrontBufferedStream::isAtEnd() const {
81    if (fOffset < fBufferedSoFar) {
82        // Even if the underlying stream is at the end, this stream has been
83        // rewound after buffering, so it is not at the end.
84        return false;
85    }
86
87    return fStream->isAtEnd();
88}
89
90bool FrontBufferedStream::rewind() {
91    // Only allow a rewind if we have not exceeded the buffer.
92    if (fOffset <= fBufferSize) {
93        fOffset = 0;
94        return true;
95    }
96    return false;
97}
98
99size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
100    SkASSERT(fOffset < fBufferedSoFar);
101    // Some data has already been copied to fBuffer. Read up to the
102    // lesser of the size requested and the remainder of the buffered
103    // data.
104    const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
105    if (dst != nullptr) {
106        memcpy(dst, fBuffer + fOffset, bytesToCopy);
107    }
108
109    // Update fOffset to the new position. It is guaranteed to be
110    // within the buffered data.
111    fOffset += bytesToCopy;
112    SkASSERT(fOffset <= fBufferedSoFar);
113
114    return bytesToCopy;
115}
116
117size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
118    SkASSERT(size > 0);
119    SkASSERT(fOffset >= fBufferedSoFar);
120    SkASSERT(fBuffer);
121    // Data needs to be buffered. Buffer up to the lesser of the size requested
122    // and the remainder of the max buffer size.
123    const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
124    char* buffer = fBuffer + fOffset;
125    const size_t buffered = fStream->read(buffer, bytesToBuffer);
126
127    fBufferedSoFar += buffered;
128    fOffset = fBufferedSoFar;
129    SkASSERT(fBufferedSoFar <= fBufferSize);
130
131    // Copy the buffer to the destination buffer and update the amount read.
132    if (dst != nullptr) {
133        memcpy(dst, buffer, buffered);
134    }
135
136    return buffered;
137}
138
139size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
140    SkASSERT(size > 0);
141    // If we get here, we have buffered all that can be buffered.
142    SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
143
144    const size_t bytesReadDirectly = fStream->read(dst, size);
145    fOffset += bytesReadDirectly;
146
147    // If we have read past the end of the buffer, rewinding is no longer
148    // supported, so we can go ahead and free the memory.
149    if (bytesReadDirectly > 0) {
150        sk_free(fBuffer.release());
151    }
152
153    return bytesReadDirectly;
154}
155
156size_t FrontBufferedStream::peek(void* dst, size_t size) const {
157    // Keep track of the offset so we can return to it.
158    const size_t start = fOffset;
159
160    if (start >= fBufferSize) {
161        // This stream is not able to buffer.
162        return 0;
163    }
164
165    size = SkTMin(size, fBufferSize - start);
166    FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
167    const size_t bytesRead = nonConstThis->read(dst, size);
168    nonConstThis->fOffset = start;
169    return bytesRead;
170}
171
172size_t FrontBufferedStream::read(void* voidDst, size_t size) {
173    // Cast voidDst to a char* for easy addition.
174    char* dst = reinterpret_cast<char*>(voidDst);
175    SkDEBUGCODE(const size_t totalSize = size;)
176    const size_t start = fOffset;
177
178    // First, read any data that was previously buffered.
179    if (fOffset < fBufferedSoFar) {
180        const size_t bytesCopied = this->readFromBuffer(dst, size);
181
182        // Update the remaining number of bytes needed to read
183        // and the destination buffer.
184        size -= bytesCopied;
185        SkASSERT(size + (fOffset - start) == totalSize);
186        if (dst != nullptr) {
187            dst += bytesCopied;
188        }
189    }
190
191    // Buffer any more data that should be buffered, and copy it to the
192    // destination.
193    if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
194        const size_t buffered = this->bufferAndWriteTo(dst, size);
195
196        // Update the remaining number of bytes needed to read
197        // and the destination buffer.
198        size -= buffered;
199        SkASSERT(size + (fOffset - start) == totalSize);
200        if (dst != nullptr) {
201            dst += buffered;
202        }
203    }
204
205    if (size > 0 && !fStream->isAtEnd()) {
206        SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
207        SkDEBUGCODE(size -= bytesReadDirectly;)
208        SkASSERT(size + (fOffset - start) == totalSize);
209    }
210
211    return fOffset - start;
212}
213