aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/SkFrontBufferedStream.cpp
blob: 9205d104a305711a7a2ee8f4c1d1a319ee41c278 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFrontBufferedStream.h"
#include "SkStream.h"
#include "SkTemplates.h"

class FrontBufferedStream : public SkStreamRewindable {
public:
    // Called by Make.
    FrontBufferedStream(std::unique_ptr<SkStream>, size_t bufferSize);

    size_t read(void* buffer, size_t size) override;

    size_t peek(void* buffer, size_t size) const override;

    bool isAtEnd() const override;

    bool rewind() override;

    bool hasLength() const override { return fHasLength; }

    size_t getLength() const override { return fLength; }

private:
    SkStreamRewindable* onDuplicate() const override { return nullptr; }

    std::unique_ptr<SkStream> fStream;
    const bool                fHasLength;
    const size_t              fLength;
    // Current offset into the stream. Always >= 0.
    size_t                    fOffset;
    // Amount that has been buffered by calls to read. Will always be less than
    // fBufferSize.
    size_t                    fBufferedSoFar;
    // Total size of the buffer.
    const size_t              fBufferSize;
    // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
    // nullptr stream.
    SkAutoTMalloc<char>       fBuffer;

    // Read up to size bytes from already buffered data, and copy to
    // dst, if non-nullptr. Updates fOffset. Assumes that fOffset is less
    // than fBufferedSoFar.
    size_t readFromBuffer(char* dst, size_t size);

    // Buffer up to size bytes from the stream, and copy to dst if non-
    // nullptr. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
    // less than fBufferedSoFar, and size is greater than 0.
    size_t bufferAndWriteTo(char* dst, size_t size);

    // Read up to size bytes directly from the stream and into dst if non-
    // nullptr. Updates fOffset. Assumes fOffset is at or beyond the buffered
    // data, and size is greater than 0.
    size_t readDirectlyFromStream(char* dst, size_t size);

    typedef SkStream INHERITED;
};

std::unique_ptr<SkStreamRewindable> SkFrontBufferedStream::Make(std::unique_ptr<SkStream> stream,
                                                                size_t bufferSize) {
    if (!stream) {
        return nullptr;
    }
    return std::unique_ptr<SkStreamRewindable>(new FrontBufferedStream(std::move(stream),
                                                                       bufferSize));
}

FrontBufferedStream::FrontBufferedStream(std::unique_ptr<SkStream> stream, size_t bufferSize)
    : fStream(std::move(stream))
    , fHasLength(fStream->hasPosition() && fStream->hasLength())
    , fLength(fStream->getLength() - fStream->getPosition())
    , fOffset(0)
    , fBufferedSoFar(0)
    , fBufferSize(bufferSize)
    , fBuffer(bufferSize) {}

bool FrontBufferedStream::isAtEnd() const {
    if (fOffset < fBufferedSoFar) {
        // Even if the underlying stream is at the end, this stream has been
        // rewound after buffering, so it is not at the end.
        return false;
    }

    return fStream->isAtEnd();
}

bool FrontBufferedStream::rewind() {
    // Only allow a rewind if we have not exceeded the buffer.
    if (fOffset <= fBufferSize) {
        fOffset = 0;
        return true;
    }
    return false;
}

size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
    SkASSERT(fOffset < fBufferedSoFar);
    // Some data has already been copied to fBuffer. Read up to the
    // lesser of the size requested and the remainder of the buffered
    // data.
    const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
    if (dst != nullptr) {
        memcpy(dst, fBuffer + fOffset, bytesToCopy);
    }

    // Update fOffset to the new position. It is guaranteed to be
    // within the buffered data.
    fOffset += bytesToCopy;
    SkASSERT(fOffset <= fBufferedSoFar);

    return bytesToCopy;
}

size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
    SkASSERT(size > 0);
    SkASSERT(fOffset >= fBufferedSoFar);
    SkASSERT(fBuffer);
    // Data needs to be buffered. Buffer up to the lesser of the size requested
    // and the remainder of the max buffer size.
    const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
    char* buffer = fBuffer + fOffset;
    const size_t buffered = fStream->read(buffer, bytesToBuffer);

    fBufferedSoFar += buffered;
    fOffset = fBufferedSoFar;
    SkASSERT(fBufferedSoFar <= fBufferSize);

    // Copy the buffer to the destination buffer and update the amount read.
    if (dst != nullptr) {
        memcpy(dst, buffer, buffered);
    }

    return buffered;
}

size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
    SkASSERT(size > 0);
    // If we get here, we have buffered all that can be buffered.
    SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);

    const size_t bytesReadDirectly = fStream->read(dst, size);
    fOffset += bytesReadDirectly;

    // If we have read past the end of the buffer, rewinding is no longer
    // supported, so we can go ahead and free the memory.
    if (bytesReadDirectly > 0) {
        sk_free(fBuffer.release());
    }

    return bytesReadDirectly;
}

size_t FrontBufferedStream::peek(void* dst, size_t size) const {
    // Keep track of the offset so we can return to it.
    const size_t start = fOffset;

    if (start >= fBufferSize) {
        // This stream is not able to buffer.
        return 0;
    }

    size = SkTMin(size, fBufferSize - start);
    FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
    const size_t bytesRead = nonConstThis->read(dst, size);
    nonConstThis->fOffset = start;
    return bytesRead;
}

size_t FrontBufferedStream::read(void* voidDst, size_t size) {
    // Cast voidDst to a char* for easy addition.
    char* dst = reinterpret_cast<char*>(voidDst);
    SkDEBUGCODE(const size_t totalSize = size;)
    const size_t start = fOffset;

    // First, read any data that was previously buffered.
    if (fOffset < fBufferedSoFar) {
        const size_t bytesCopied = this->readFromBuffer(dst, size);

        // Update the remaining number of bytes needed to read
        // and the destination buffer.
        size -= bytesCopied;
        SkASSERT(size + (fOffset - start) == totalSize);
        if (dst != nullptr) {
            dst += bytesCopied;
        }
    }

    // Buffer any more data that should be buffered, and copy it to the
    // destination.
    if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
        const size_t buffered = this->bufferAndWriteTo(dst, size);

        // Update the remaining number of bytes needed to read
        // and the destination buffer.
        size -= buffered;
        SkASSERT(size + (fOffset - start) == totalSize);
        if (dst != nullptr) {
            dst += buffered;
        }
    }

    if (size > 0 && !fStream->isAtEnd()) {
        SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
        SkDEBUGCODE(size -= bytesReadDirectly;)
        SkASSERT(size + (fOffset - start) == totalSize);
    }

    return fOffset - start;
}