OpenShot Audio Library | OpenShotAudio 0.4.0
juce_BufferedInputStream.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
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 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 bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()),
44 position (bufferedRange.getStart()),
45 bufferLength (calcBufferStreamBufferSize (size, sourceStream))
46{
47 buffer.malloc (bufferLength);
48}
49
51 : BufferedInputStream (&sourceStream, size, false)
52{
53}
54
56
57//==============================================================================
59{
60 if (! ensureBuffered())
61 return 0;
62
63 return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0;
64}
65
67{
68 return source->getTotalLength();
69}
70
72{
73 return position;
74}
75
76bool BufferedInputStream::setPosition (int64 newPosition)
77{
78 position = jmax ((int64) 0, newPosition);
79 return true;
80}
81
83{
84 return position >= lastReadPos && source->isExhausted();
85}
86
87bool BufferedInputStream::ensureBuffered()
88{
89 auto bufferEndOverlap = lastReadPos - bufferOverlap;
90
91 if (position < bufferedRange.getStart() || position >= bufferEndOverlap)
92 {
93 int bytesRead = 0;
94
95 if (position < lastReadPos
96 && position >= bufferEndOverlap
97 && position >= bufferedRange.getStart())
98 {
99 auto bytesToKeep = (int) (lastReadPos - position);
100 memmove (buffer, buffer + (int) (position - bufferedRange.getStart()), (size_t) bytesToKeep);
101
102 bytesRead = source->read (buffer + bytesToKeep,
103 (int) (bufferLength - bytesToKeep));
104
105 if (bytesRead < 0)
106 return false;
107
108 lastReadPos += bytesRead;
109 bytesRead += bytesToKeep;
110 }
111 else
112 {
113 if (! source->setPosition (position))
114 return false;
115
116 bytesRead = (int) source->read (buffer, (size_t) bufferLength);
117
118 if (bytesRead < 0)
119 return false;
120
121 lastReadPos = position + bytesRead;
122 }
123
124 bufferedRange = Range<int64> (position, lastReadPos);
125
126 while (bytesRead < bufferLength)
127 buffer[bytesRead++] = 0;
128 }
129
130 return true;
131}
132
133int BufferedInputStream::read (void* destBuffer, const int maxBytesToRead)
134{
135 const auto initialPosition = position;
136
137 const auto getBufferedRange = [this] { return bufferedRange; };
138
139 const auto readFromReservoir = [this, &destBuffer, &initialPosition] (const Range<int64> rangeToRead)
140 {
141 memcpy (static_cast<char*> (destBuffer) + (rangeToRead.getStart() - initialPosition),
142 buffer + (rangeToRead.getStart() - bufferedRange.getStart()),
143 (size_t) rangeToRead.getLength());
144 };
145
146 const auto fillReservoir = [this] (int64 requestedStart)
147 {
148 position = requestedStart;
149 ensureBuffered();
150 };
151
152 const auto remaining = Reservoir::doBufferedRead (Range<int64> (position, position + maxBytesToRead),
153 getBufferedRange,
154 readFromReservoir,
155 fillReservoir);
156
157 const auto bytesRead = maxBytesToRead - remaining.getLength();
158 position = remaining.getStart();
159 return (int) bytesRead;
160}
161
163{
164 if (position >= bufferedRange.getStart()
165 && position < lastReadPos)
166 {
167 auto maxChars = (int) (lastReadPos - position);
168 auto* src = buffer + (int) (position - bufferedRange.getStart());
169
170 for (int i = 0; i < maxChars; ++i)
171 {
172 if (src[i] == 0)
173 {
174 position += i + 1;
175 return String::fromUTF8 (src, i);
176 }
177 }
178 }
179
181}
182
183
184//==============================================================================
185//==============================================================================
186#if JUCE_UNIT_TESTS
187
188struct BufferedInputStreamTests final : public UnitTest
189{
190 template <typename Fn, size_t... Ix, typename Values>
191 static void applyImpl (Fn&& fn, std::index_sequence<Ix...>, Values&& values)
192 {
193 fn (std::get<Ix> (values)...);
194 }
195
196 template <typename Fn, typename... Values>
197 static void apply (Fn&& fn, std::tuple<Values...> values)
198 {
199 applyImpl (fn, std::make_index_sequence<sizeof... (Values)>(), values);
200 }
201
202 template <typename Fn, typename Values>
203 static void allCombinationsImpl (Fn&& fn, Values&& values)
204 {
205 apply (fn, values);
206 }
207
208 template <typename Fn, typename Values, typename Range, typename... Ranges>
209 static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges)
210 {
211 for (auto& item : range)
212 allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...);
213 }
214
215 template <typename Fn, typename... Ranges>
216 static void allCombinations (Fn&& fn, Ranges&&... ranges)
217 {
218 allCombinationsImpl (fn, std::tie(), ranges...);
219 }
220
221 BufferedInputStreamTests()
222 : UnitTest ("BufferedInputStream", UnitTestCategories::streams)
223 {}
224
225 void runTest() override
226 {
227 const MemoryBlock testBufferA ("abcdefghijklmnopqrstuvwxyz", 26);
228
229 const auto testBufferB = [&]
230 {
231 MemoryBlock mb { 8192 };
232 auto r = getRandom();
233
234 std::for_each (mb.begin(), mb.end(), [&] (char& item)
235 {
236 item = (char) r.nextInt (std::numeric_limits<char>::max());
237 });
238
239 return mb;
240 }();
241
242 const MemoryBlock buffers[] { testBufferA, testBufferB };
243 const int readSizes[] { 3, 10, 50 };
244 const bool shouldPeek[] { false, true };
245
246 const auto runTest = [this] (const MemoryBlock& data, const int readSize, const bool peek)
247 {
248 MemoryInputStream mi (data, true);
249
250 BufferedInputStream stream (mi, jmin (200, (int) data.getSize()));
251
252 beginTest ("Read");
253
254 expectEquals (stream.getPosition(), (int64) 0);
255 expectEquals (stream.getTotalLength(), (int64) data.getSize());
256 expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
257 expect (! stream.isExhausted());
258
259 size_t numBytesRead = 0;
260 MemoryBlock readBuffer (data.getSize());
261
262 while (numBytesRead < data.getSize())
263 {
264 if (peek)
265 expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
266
267 const auto startingPos = numBytesRead;
268 numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize);
269
270 expect (std::equal (readBuffer.begin() + startingPos,
271 readBuffer.begin() + numBytesRead,
272 data.begin() + startingPos,
273 data.begin() + numBytesRead));
274 expectEquals (stream.getPosition(), (int64) numBytesRead);
275 expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
276 expect (stream.isExhausted() == (numBytesRead == data.getSize()));
277 }
278
279 expectEquals (stream.getPosition(), (int64) data.getSize());
280 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
281 expect (stream.isExhausted());
282
283 expect (readBuffer == data);
284
285 beginTest ("Skip");
286
287 stream.setPosition (0);
288 expectEquals (stream.getPosition(), (int64) 0);
289 expectEquals (stream.getTotalLength(), (int64) data.getSize());
290 expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
291 expect (! stream.isExhausted());
292
293 numBytesRead = 0;
294 const int numBytesToSkip = 5;
295
296 while (numBytesRead < data.getSize())
297 {
298 expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
299
300 stream.skipNextBytes (numBytesToSkip);
301 numBytesRead += numBytesToSkip;
302 numBytesRead = std::min (numBytesRead, data.getSize());
303
304 expectEquals (stream.getPosition(), (int64) numBytesRead);
305 expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
306 expect (stream.isExhausted() == (numBytesRead == data.getSize()));
307 }
308
309 expectEquals (stream.getPosition(), (int64) data.getSize());
310 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
311 expect (stream.isExhausted());
312 };
313
314 allCombinations (runTest, buffers, readSizes, shouldPeek);
315 }
316};
317
318static BufferedInputStreamTests bufferedInputStreamTests;
319
320#endif
321
322} // namespace juce
BufferedInputStream(InputStream *sourceStream, int bufferSize, bool deleteSourceWhenDestroyed)
int read(void *destBuffer, int maxBytesToRead) override
bool setPosition(int64 newPosition) override
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
virtual String readString()
constexpr ValueType getStart() const noexcept
Definition: juce_Range.h:80
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)