• 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     bool peek(void* buffer, size_t size) const override;
20 
21     bool isAtEnd() const override;
22 
23     bool rewind() override;
24 
hasPosition() const25     bool hasPosition() const override { return true; }
26 
getPosition() const27     size_t getPosition() const override { return fOffset; }
28 
hasLength() const29     bool hasLength() const override { return fHasLength; }
30 
getLength() const31     size_t getLength() const override { return fLength; }
32 
duplicate() const33     SkStreamRewindable* duplicate() const override { return NULL; }
34 
35 private:
36     SkAutoTDelete<SkStream> fStream;
37     const bool              fHasLength;
38     const size_t            fLength;
39     // Current offset into the stream. Always >= 0.
40     size_t                  fOffset;
41     // Amount that has been buffered by calls to read. Will always be less than
42     // fBufferSize.
43     size_t                  fBufferedSoFar;
44     // Total size of the buffer.
45     const size_t            fBufferSize;
46     // FIXME: SkAutoTMalloc throws on failure. Instead, Create should return a
47     // NULL stream.
48     SkAutoTMalloc<char>     fBuffer;
49 
50     // Read up to size bytes from already buffered data, and copy to
51     // dst, if non-NULL. Updates fOffset. Assumes that fOffset is less
52     // than fBufferedSoFar.
53     size_t readFromBuffer(char* dst, size_t size);
54 
55     // Buffer up to size bytes from the stream, and copy to dst if non-
56     // NULL. Updates fOffset and fBufferedSoFar. Assumes that fOffset is
57     // less than fBufferedSoFar, and size is greater than 0.
58     size_t bufferAndWriteTo(char* dst, size_t size);
59 
60     // Read up to size bytes directly from the stream and into dst if non-
61     // NULL. Updates fOffset. Assumes fOffset is at or beyond the buffered
62     // data, and size is greater than 0.
63     size_t readDirectlyFromStream(char* dst, size_t size);
64 
65     typedef SkStream INHERITED;
66 };
67 
Create(SkStream * stream,size_t bufferSize)68 SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
69     if (NULL == stream) {
70         return NULL;
71     }
72     return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize));
73 }
74 
FrontBufferedStream(SkStream * stream,size_t bufferSize)75 FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
76     : fStream(stream)
77     , fHasLength(stream->hasPosition() && stream->hasLength())
78     , fLength(stream->getLength() - stream->getPosition())
79     , fOffset(0)
80     , fBufferedSoFar(0)
81     , fBufferSize(bufferSize)
82     , fBuffer(bufferSize) {}
83 
isAtEnd() const84 bool FrontBufferedStream::isAtEnd() const {
85     if (fOffset < fBufferedSoFar) {
86         // Even if the underlying stream is at the end, this stream has been
87         // rewound after buffering, so it is not at the end.
88         return false;
89     }
90 
91     return fStream->isAtEnd();
92 }
93 
rewind()94 bool FrontBufferedStream::rewind() {
95     // Only allow a rewind if we have not exceeded the buffer.
96     if (fOffset <= fBufferSize) {
97         fOffset = 0;
98         return true;
99     }
100     return false;
101 }
102 
readFromBuffer(char * dst,size_t size)103 size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
104     SkASSERT(fOffset < fBufferedSoFar);
105     // Some data has already been copied to fBuffer. Read up to the
106     // lesser of the size requested and the remainder of the buffered
107     // data.
108     const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
109     if (dst != NULL) {
110         memcpy(dst, fBuffer + fOffset, bytesToCopy);
111     }
112 
113     // Update fOffset to the new position. It is guaranteed to be
114     // within the buffered data.
115     fOffset += bytesToCopy;
116     SkASSERT(fOffset <= fBufferedSoFar);
117 
118     return bytesToCopy;
119 }
120 
bufferAndWriteTo(char * dst,size_t size)121 size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
122     SkASSERT(size > 0);
123     SkASSERT(fOffset >= fBufferedSoFar);
124     SkASSERT(fBuffer);
125     // Data needs to be buffered. Buffer up to the lesser of the size requested
126     // and the remainder of the max buffer size.
127     const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
128     char* buffer = fBuffer + fOffset;
129     const size_t buffered = fStream->read(buffer, bytesToBuffer);
130 
131     fBufferedSoFar += buffered;
132     fOffset = fBufferedSoFar;
133     SkASSERT(fBufferedSoFar <= fBufferSize);
134 
135     // Copy the buffer to the destination buffer and update the amount read.
136     if (dst != NULL) {
137         memcpy(dst, buffer, buffered);
138     }
139 
140     return buffered;
141 }
142 
readDirectlyFromStream(char * dst,size_t size)143 size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
144     SkASSERT(size > 0);
145     // If we get here, we have buffered all that can be buffered.
146     SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
147 
148     const size_t bytesReadDirectly = fStream->read(dst, size);
149     fOffset += bytesReadDirectly;
150 
151     // If we have read past the end of the buffer, rewinding is no longer
152     // supported, so we can go ahead and free the memory.
153     if (bytesReadDirectly > 0) {
154         sk_free(fBuffer.detach());
155     }
156 
157     return bytesReadDirectly;
158 }
159 
peek(void * dst,size_t size) const160 bool FrontBufferedStream::peek(void* dst, size_t size) const {
161     // Keep track of the offset so we can return to it.
162     const size_t start = fOffset;
163     if (start + size > fBufferSize) {
164         // This stream is not able to buffer enough.
165         return false;
166     }
167     FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
168     SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(dst, size);
169     SkASSERT(bytesRead == size);
170     nonConstThis->fOffset = start;
171     return true;
172 }
173 
read(void * voidDst,size_t size)174 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
175     // Cast voidDst to a char* for easy addition.
176     char* dst = reinterpret_cast<char*>(voidDst);
177     SkDEBUGCODE(const size_t totalSize = size;)
178     const size_t start = fOffset;
179 
180     // First, read any data that was previously buffered.
181     if (fOffset < fBufferedSoFar) {
182         const size_t bytesCopied = this->readFromBuffer(dst, size);
183 
184         // Update the remaining number of bytes needed to read
185         // and the destination buffer.
186         size -= bytesCopied;
187         SkASSERT(size + (fOffset - start) == totalSize);
188         if (dst != NULL) {
189             dst += bytesCopied;
190         }
191     }
192 
193     // Buffer any more data that should be buffered, and copy it to the
194     // destination.
195     if (size > 0 && fBufferedSoFar < fBufferSize && !fStream->isAtEnd()) {
196         const size_t buffered = this->bufferAndWriteTo(dst, size);
197 
198         // Update the remaining number of bytes needed to read
199         // and the destination buffer.
200         size -= buffered;
201         SkASSERT(size + (fOffset - start) == totalSize);
202         if (dst != NULL) {
203             dst += buffered;
204         }
205     }
206 
207     if (size > 0 && !fStream->isAtEnd()) {
208         SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
209         SkDEBUGCODE(size -= bytesReadDirectly;)
210         SkASSERT(size + (fOffset - start) == totalSize);
211     }
212 
213     return fOffset - start;
214 }
215