blob: d44389b7c81ad4c8bdfd625b4f0c29d2625a6742 [file] [log] [blame]
scroggo@google.com83fd2c72013-09-26 21:35:39 +00001/*
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"
scroggo@google.com09a53832013-11-12 20:53:05 +00009#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;
28
29 virtual size_t getLength() const SK_OVERRIDE;
30
31 virtual SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
32
33private:
34 SkAutoTUnref<SkStream> fStream;
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 // NULL stream.
44 SkAutoTMalloc<char> fBuffer;
45
46 // Read up to size bytes from already buffered data, and copy to
47 // dst, if non-NULL. 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 // NULL. 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 // NULL. 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};
scroggo@google.com83fd2c72013-09-26 21:35:39 +000063
64SkStreamRewindable* SkFrontBufferedStream::Create(SkStream* stream, size_t bufferSize) {
65 if (NULL == stream) {
66 return NULL;
67 }
scroggo@google.com09a53832013-11-12 20:53:05 +000068 return SkNEW_ARGS(FrontBufferedStream, (stream, bufferSize));
scroggo@google.com83fd2c72013-09-26 21:35:39 +000069}
70
scroggo@google.com09a53832013-11-12 20:53:05 +000071FrontBufferedStream::FrontBufferedStream(SkStream* stream, size_t bufferSize)
scroggo@google.com83fd2c72013-09-26 21:35:39 +000072 : fStream(SkRef(stream))
73 , fOffset(0)
74 , fBufferedSoFar(0)
75 , fBufferSize(bufferSize)
76 , fBuffer(bufferSize) {}
77
scroggo@google.com09a53832013-11-12 20:53:05 +000078bool FrontBufferedStream::isAtEnd() const {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000079 if (fOffset < fBufferedSoFar) {
80 // Even if the underlying stream is at the end, this stream has been
81 // rewound after buffering, so it is not at the end.
82 return false;
83 }
84
85 return fStream->isAtEnd();
86}
87
scroggo@google.com09a53832013-11-12 20:53:05 +000088bool FrontBufferedStream::rewind() {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000089 // Only allow a rewind if we have not exceeded the buffer.
90 if (fOffset <= fBufferSize) {
91 fOffset = 0;
92 return true;
93 }
94 return false;
95}
96
scroggo@google.com09a53832013-11-12 20:53:05 +000097bool FrontBufferedStream::hasLength() const {
scroggo@google.com83fd2c72013-09-26 21:35:39 +000098 return fStream->hasLength();
99}
100
scroggo@google.com09a53832013-11-12 20:53:05 +0000101size_t FrontBufferedStream::getLength() const {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000102 return fStream->getLength();
103}
104
scroggo@google.com09a53832013-11-12 20:53:05 +0000105size_t FrontBufferedStream::readFromBuffer(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000106 SkASSERT(fOffset < fBufferedSoFar);
107 // Some data has already been copied to fBuffer. Read up to the
108 // lesser of the size requested and the remainder of the buffered
109 // data.
110 const size_t bytesToCopy = SkTMin(size, fBufferedSoFar - fOffset);
111 if (dst != NULL) {
112 memcpy(dst, fBuffer + fOffset, bytesToCopy);
113 }
114
115 // Update fOffset to the new position. It is guaranteed to be
116 // within the buffered data.
117 fOffset += bytesToCopy;
118 SkASSERT(fOffset <= fBufferedSoFar);
119
120 return bytesToCopy;
121}
122
scroggo@google.com09a53832013-11-12 20:53:05 +0000123size_t FrontBufferedStream::bufferAndWriteTo(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000124 SkASSERT(size > 0);
125 SkASSERT(fOffset >= fBufferedSoFar);
126 // Data needs to be buffered. Buffer up to the lesser of the size requested
127 // and the remainder of the max buffer size.
128 const size_t bytesToBuffer = SkTMin(size, fBufferSize - fBufferedSoFar);
129 char* buffer = fBuffer + fOffset;
130 const size_t buffered = fStream->read(buffer, bytesToBuffer);
131
132 fBufferedSoFar += buffered;
133 fOffset = fBufferedSoFar;
134 SkASSERT(fBufferedSoFar <= fBufferSize);
135
136 // Copy the buffer to the destination buffer and update the amount read.
137 if (dst != NULL) {
138 memcpy(dst, buffer, buffered);
139 }
140
141 return buffered;
142}
143
scroggo@google.com09a53832013-11-12 20:53:05 +0000144size_t FrontBufferedStream::readDirectlyFromStream(char* dst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000145 SkASSERT(size > 0);
146 // If we get here, we have buffered all that can be buffered.
147 SkASSERT(fBufferSize == fBufferedSoFar && fOffset >= fBufferSize);
148
149 const size_t bytesReadDirectly = fStream->read(dst, size);
150 fOffset += bytesReadDirectly;
151
152 // If we have read past the end of the buffer, rewinding is no longer
153 // supported, so we can go ahead and free the memory.
154 if (bytesReadDirectly > 0) {
155 fBuffer.reset(0);
156 }
157
158 return bytesReadDirectly;
159}
160
scroggo@google.com09a53832013-11-12 20:53:05 +0000161size_t FrontBufferedStream::read(void* voidDst, size_t size) {
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000162 // Cast voidDst to a char* for easy addition.
163 char* dst = reinterpret_cast<char*>(voidDst);
164 SkDEBUGCODE(const size_t totalSize = size;)
165 const size_t start = fOffset;
166
167 // First, read any data that was previously buffered.
168 if (fOffset < fBufferedSoFar) {
169 const size_t bytesCopied = this->readFromBuffer(dst, size);
170
171 // Update the remaining number of bytes needed to read
172 // and the destination buffer.
173 size -= bytesCopied;
174 SkASSERT(size + (fOffset - start) == totalSize);
175 if (dst != NULL) {
176 dst += bytesCopied;
177 }
178 }
179
180 // Buffer any more data that should be buffered, and copy it to the
181 // destination.
182 if (size > 0 && fBufferedSoFar < fBufferSize) {
183 const size_t buffered = this->bufferAndWriteTo(dst, size);
184
185 // Update the remaining number of bytes needed to read
186 // and the destination buffer.
187 size -= buffered;
188 SkASSERT(size + (fOffset - start) == totalSize);
189 if (dst != NULL) {
190 dst += buffered;
191 }
192 }
193
194 if (size > 0 && !fStream->isAtEnd()) {
scroggo@google.comfd67f2f2013-09-26 21:49:46 +0000195 SkDEBUGCODE(const size_t bytesReadDirectly =) this->readDirectlyFromStream(dst, size);
scroggo@google.com83fd2c72013-09-26 21:35:39 +0000196 SkDEBUGCODE(size -= bytesReadDirectly;)
197 SkASSERT(size + (fOffset - start) == totalSize);
198 }
199
200 return fOffset - start;
201}