OpenShot Library | libopenshot-audio 0.2.0
juce_BufferedInputStream.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26static inline int calcBufferStreamBufferSize (int requestedSize, InputStream* source) noexcept
27{
28 // You need to supply a real stream when creating a BufferedInputStream
29 jassert (source != nullptr);
30
31 requestedSize = jmax (256, requestedSize);
32 auto sourceSize = source->getTotalLength();
33
34 if (sourceSize >= 0 && sourceSize < requestedSize)
35 return jmax (32, (int) sourceSize);
36
37 return requestedSize;
38}
39
40//==============================================================================
41BufferedInputStream::BufferedInputStream (InputStream* sourceStream, int size, bool takeOwnership)
42 : source (sourceStream, takeOwnership),
43 bufferSize (calcBufferStreamBufferSize (size, sourceStream)),
44 position (sourceStream->getPosition()),
45 bufferStart (position)
46{
47 buffer.malloc (bufferSize);
48}
49
51 : BufferedInputStream (&sourceStream, size, false)
52{
53}
54
56{
57}
58
59//==============================================================================
61{
62 if (! ensureBuffered())
63 return 0;
64
65 return position < lastReadPos ? buffer[(int) (position - bufferStart)] : 0;
66}
67
69{
70 return source->getTotalLength();
71}
72
74{
75 return position;
76}
77
78bool BufferedInputStream::setPosition (int64 newPosition)
79{
80 position = jmax ((int64) 0, newPosition);
81 return true;
82}
83
85{
86 return position >= lastReadPos && source->isExhausted();
87}
88
89bool BufferedInputStream::ensureBuffered()
90{
91 auto bufferEndOverlap = lastReadPos - bufferOverlap;
92
93 if (position < bufferStart || position >= bufferEndOverlap)
94 {
95 int bytesRead;
96
97 if (position < lastReadPos
98 && position >= bufferEndOverlap
99 && position >= bufferStart)
100 {
101 auto bytesToKeep = (int) (lastReadPos - position);
102 memmove (buffer, buffer + (int) (position - bufferStart), (size_t) bytesToKeep);
103
104 bufferStart = position;
105 bytesRead = source->read (buffer + bytesToKeep,
106 (int) (bufferSize - bytesToKeep));
107
108 if (bytesRead < 0)
109 return false;
110
111 lastReadPos += bytesRead;
112 bytesRead += bytesToKeep;
113 }
114 else
115 {
116 bufferStart = position;
117
118 if (! source->setPosition (bufferStart))
119 return false;
120
121 bytesRead = source->read (buffer, bufferSize);
122
123 if (bytesRead < 0)
124 return false;
125
126 lastReadPos = bufferStart + bytesRead;
127 }
128
129 while (bytesRead < bufferSize)
130 buffer[bytesRead++] = 0;
131 }
132
133 return true;
134}
135
136int BufferedInputStream::read (void* destBuffer, int maxBytesToRead)
137{
138 jassert (destBuffer != nullptr && maxBytesToRead >= 0);
139
140 if (position >= bufferStart
141 && position + maxBytesToRead <= lastReadPos)
142 {
143 memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) maxBytesToRead);
144 position += maxBytesToRead;
145 return maxBytesToRead;
146 }
147
148 if (position < bufferStart || position >= lastReadPos)
149 if (! ensureBuffered())
150 return 0;
151
152 int bytesRead = 0;
153
154 while (maxBytesToRead > 0)
155 {
156 auto numToRead = jmin (maxBytesToRead, (int) (lastReadPos - position));
157
158 if (numToRead > 0)
159 {
160 memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) numToRead);
161 maxBytesToRead -= numToRead;
162 bytesRead += numToRead;
163 position += numToRead;
164 destBuffer = static_cast<char*> (destBuffer) + numToRead;
165 }
166
167 auto oldLastReadPos = lastReadPos;
168
169 if (! ensureBuffered()
170 || oldLastReadPos == lastReadPos
171 || isExhausted())
172 break;
173 }
174
175 return bytesRead;
176}
177
179{
180 if (position >= bufferStart
181 && position < lastReadPos)
182 {
183 auto maxChars = (int) (lastReadPos - position);
184 auto* src = buffer + (int) (position - bufferStart);
185
186 for (int i = 0; i < maxChars; ++i)
187 {
188 if (src[i] == 0)
189 {
190 position += i + 1;
191 return String::fromUTF8 (src, i);
192 }
193 }
194 }
195
197}
198
199//==============================================================================
200#if JUCE_UNIT_TESTS
201
202struct BufferedInputStreamTests : public UnitTest
203{
204 BufferedInputStreamTests()
205 : UnitTest ("BufferedInputStream", "Streams")
206 {}
207
208 void runTest() override
209 {
210 const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26);
211 MemoryInputStream mi (data, true);
212
213 BufferedInputStream stream (mi, (int) data.getSize());
214
215 beginTest ("Read");
216
217 expectEquals (stream.getPosition(), (int64) 0);
218 expectEquals (stream.getTotalLength(), (int64) data.getSize());
219 expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
220 expect (! stream.isExhausted());
221
222 size_t numBytesRead = 0;
223 MemoryBlock readBuffer (data.getSize());
224
225 while (numBytesRead < data.getSize())
226 {
227 expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
228
229 numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3);
230
231 expectEquals (stream.getPosition(), (int64) numBytesRead);
232 expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
233 expect (stream.isExhausted() == (numBytesRead == data.getSize()));
234 }
235
236 expectEquals (stream.getPosition(), (int64) data.getSize());
237 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
238 expect (stream.isExhausted());
239
240 expect (readBuffer == data);
241
242 beginTest ("Skip");
243
244 stream.setPosition (0);
245 expectEquals (stream.getPosition(), (int64) 0);
246 expectEquals (stream.getTotalLength(), (int64) data.getSize());
247 expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
248 expect (! stream.isExhausted());
249
250 numBytesRead = 0;
251 const int numBytesToSkip = 5;
252
253 while (numBytesRead < data.getSize())
254 {
255 expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
256
257 stream.skipNextBytes (numBytesToSkip);
258 numBytesRead += numBytesToSkip;
259 numBytesRead = std::min (numBytesRead, data.getSize());
260
261 expectEquals (stream.getPosition(), (int64) numBytesRead);
262 expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
263 expect (stream.isExhausted() == (numBytesRead == data.getSize()));
264 }
265
266 expectEquals (stream.getPosition(), (int64) data.getSize());
267 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
268 expect (stream.isExhausted());
269 }
270};
271
272static BufferedInputStreamTests bufferedInputStreamTests;
273
274#endif
275
276} // namespace juce
Wraps another input stream, and reads from it using an intermediate buffer.
~BufferedInputStream() override
Destructor.
BufferedInputStream(InputStream *sourceStream, int bufferSize, bool deleteSourceWhenDestroyed)
Creates a BufferedInputStream from an input source.
int64 getPosition() override
Returns the offset of the next byte that will be read from the stream.
char peekByte()
Returns the next byte that would be read by a call to readByte()
bool isExhausted() override
Returns true if the stream has no more data to read.
String readString() override
Reads a zero-terminated UTF-8 string from the stream.
int read(void *destBuffer, int maxBytesToRead) override
Reads some data from the stream into a memory buffer.
int64 getTotalLength() override
Returns the total number of bytes available for reading in this stream.
bool setPosition(int64 newPosition) override
Tries to move the current read position of the stream.
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory.
The base class for streams that read data.
virtual String readString()
Reads a zero-terminated UTF-8 string from the stream.
The JUCE String class!
Definition juce_String.h:43
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
This is a base class for classes that perform a unit test.