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    virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
18
19    virtual bool isAtEnd() const SK_OVERRIDE;
20
21    virtual bool rewind() SK_OVERRIDE;
22
23    virtual bool hasPosition() const SK_OVERRIDE { return true; }
24
25    virtual size_t getPosition() const SK_OVERRIDE { return fOffset; }
26
27    virtual bool hasLength() const SK_OVERRIDE { return fHasLength; }
28
29    virtual size_t getLength() const SK_OVERRIDE { return fLength; }
30
31    virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
32
33private:
34    SkAutoTUnref<SkStream>  fStream;
35    const bool              fHasLength;
36    const size_t            fLength;
37    // Current offset into the stream. Always >= 0.
38    size_t                  fOffset;
39    // Amount that has been buffered by calls to read. Will always be less than
40    // fBufferSize.
41    size_t                  fBufferedSoFar;
42    // Total size of the buffer.
43    const size_t            fBufferSize;
44    // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
45    // NULL stream.
46    SkAutoTMalloc<char>     fBuffer;
47
48    // Read up to size bytes from already buffered data, and copy to
49    // dst, if non-NULL. Updates fOffset. Assumes that fOffset is less
50    // than fBufferedSoFar.
51    size_t readFromBuffer(char* dst, size_t size);
52
53    // Buffer up to size bytes from the stream, and copy to dst if non-
54    // NULL. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
55    // less than fBufferedSoFar, and size is greater than 0.
56    size_t bufferAndWriteTo(char* dst, size_t size);
57
58    // Read up to size bytes directly from the stream and into dst if non-
59    // NULL. Updates fOffset. Assumes fOffset is at or beyond the buffered
60    // data, and size is greater than 0.
61    size_t readDirectlyFromStream(char* dst, size_t size);
62
63    typedef SkStream INHERITED;
64};
65
66SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
67    if (NULL == stream) {
68        return NULL;
69    }
70    return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize));
71}
72
73FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
74    : fStream(SkRef(stream))
75    , fHasLength(stream->hasPosition() && stream->hasLength())
76    , fLength(stream->getLength() - stream->getPosition())
77    , fOffset(0)
78    , fBufferedSoFar(0)
79    , fBufferSize(bufferSize)
80    , fBuffer(bufferSize) {}
81
82bool 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
92bool 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
101size_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 != NULL) {
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
119size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
120    SkASSERT(size > 0);
121    SkASSERT(fOffset >= fBufferedSoFar);
122    // Data needs to be buffered. Buffer up to the lesser of the size requested
123    // and the remainder of the max buffer size.
124    const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
125    char* buffer = fBuffer + fOffset;
126    const size_t buffered = fStream->read(buffer, bytesToBuffer);
127
128    fBufferedSoFar += buffered;
129    fOffset = fBufferedSoFar;
130    SkASSERT(fBufferedSoFar <= fBufferSize);
131
132    // Copy the buffer to the destination buffer and update the amount read.
133    if (dst != NULL) {
134        memcpy(dst, buffer, buffered);
135    }
136
137    return buffered;
138}
139
140size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
141    SkASSERT(size > 0);
142    // If we get here, we have buffered all that can be buffered.
143    SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
144
145    const size_t bytesReadDirectly = fStream->read(dst, size);
146    fOffset += bytesReadDirectly;
147
148    // If we have read past the end of the buffer, rewinding is no longer
149    // supported, so we can go ahead and free the memory.
150    if (bytesReadDirectly > 0) {
151        fBuffer.reset(0);
152    }
153
154    return bytesReadDirectly;
155}
156
157size_t FrontBufferedStream::read(void* voidDst, size_t size) {
158    // Cast voidDst to a char* for easy addition.
159    char* dst = reinterpret_cast<char*>(voidDst);
160    SkDEBUGCODE(const size_t totalSize = size;)
161    const size_t start = fOffset;
162
163    // First, read any data that was previously buffered.
164    if (fOffset < fBufferedSoFar) {
165        const size_t bytesCopied = this->readFromBuffer(dst, size);
166
167        // Update the remaining number of bytes needed to read
168        // and the destination buffer.
169        size -= bytesCopied;
170        SkASSERT(size + (fOffset - start) == totalSize);
171        if (dst != NULL) {
172            dst += bytesCopied;
173        }
174    }
175
176    // Buffer any more data that should be buffered, and copy it to the
177    // destination.
178    if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
179        const size_t buffered = this->bufferAndWriteTo(dst, size);
180
181        // Update the remaining number of bytes needed to read
182        // and the destination buffer.
183        size -= buffered;
184        SkASSERT(size + (fOffset - start) == totalSize);
185        if (dst != NULL) {
186            dst += buffered;
187        }
188    }
189
190    if (size > 0 && !fStream->isAtEnd()) {
191        SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
192        SkDEBUGCODE(size -= bytesReadDirectly;)
193        SkASSERT(size + (fOffset - start) == totalSize);
194    }
195
196    return fOffset - start;
197}
198