• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 class FrontBufferedStream : public SkStreamRewindable {
13 public:
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 
hasLength() const25     bool hasLength() const override { return fHasLength; }
26 
getLength() const27     size_t getLength() const override { return fLength; }
28 
duplicate() const29     SkStreamRewindable* duplicate() const override { return nullptr; }
30 
31 private:
32     SkAutoTDelete<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 
Create(SkStream * stream,size_t bufferSize)64 SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
65     if (nullptr == stream) {
66         return nullptr;
67     }
68     return new FrontBufferedStream(stream, bufferSize);
69 }
70 
FrontBufferedStream(SkStream * stream,size_t bufferSize)71 FrontBufferedStream::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 
isAtEnd() const80 bool 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 
rewind()90 bool 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 
readFromBuffer(char * dst,size_t size)99 size_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 
bufferAndWriteTo(char * dst,size_t size)117 size_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 
readDirectlyFromStream(char * dst,size_t size)139 size_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.detach());
151     }
152 
153     return bytesReadDirectly;
154 }
155 
peek(void * dst,size_t size) const156 size_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 
read(void * voidDst,size_t size)172 size_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