26static int calcBufferStreamBufferSize (
int requestedSize,
InputStream* source)
noexcept
29 jassert (source !=
nullptr);
31 requestedSize = jmax (256, requestedSize);
32 auto sourceSize = source->getTotalLength();
34 if (sourceSize >= 0 && sourceSize < requestedSize)
35 return jmax (32, (
int) sourceSize);
42 : source (sourceStream, takeOwnership),
44 position (bufferedRange.getStart()),
45 bufferLength (calcBufferStreamBufferSize (size, sourceStream))
47 buffer.malloc (bufferLength);
60 if (! ensureBuffered())
63 return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0;
68 return source->getTotalLength();
78 position = jmax ((int64) 0, newPosition);
84 return position >= lastReadPos && source->isExhausted();
87bool BufferedInputStream::ensureBuffered()
89 auto bufferEndOverlap = lastReadPos - bufferOverlap;
91 if (position < bufferedRange.
getStart() || position >= bufferEndOverlap)
95 if (position < lastReadPos
96 && position >= bufferEndOverlap
97 && position >= bufferedRange.
getStart())
99 auto bytesToKeep = (int) (lastReadPos - position);
100 memmove (buffer, buffer + (
int) (position - bufferedRange.
getStart()), (
size_t) bytesToKeep);
102 bytesRead = source->read (buffer + bytesToKeep,
103 (
int) (bufferLength - bytesToKeep));
108 lastReadPos += bytesRead;
109 bytesRead += bytesToKeep;
113 if (! source->setPosition (position))
116 bytesRead = (int) source->read (buffer, (
size_t) bufferLength);
121 lastReadPos = position + bytesRead;
124 bufferedRange = Range<int64> (position, lastReadPos);
126 while (bytesRead < bufferLength)
127 buffer[bytesRead++] = 0;
135 const auto initialPosition = position;
137 const auto getBufferedRange = [
this] {
return bufferedRange; };
139 const auto readFromReservoir = [
this, &destBuffer, &initialPosition] (
const Range<int64> rangeToRead)
141 memcpy (
static_cast<char*
> (destBuffer) + (rangeToRead.getStart() - initialPosition),
142 buffer + (rangeToRead.getStart() - bufferedRange.getStart()),
143 (
size_t) rangeToRead.getLength());
146 const auto fillReservoir = [
this] (int64 requestedStart)
148 position = requestedStart;
157 const auto bytesRead = maxBytesToRead - remaining.getLength();
158 position = remaining.getStart();
159 return (
int) bytesRead;
164 if (position >= bufferedRange.getStart()
165 && position < lastReadPos)
167 auto maxChars = (int) (lastReadPos - position);
168 auto* src = buffer + (int) (position - bufferedRange.getStart());
170 for (
int i = 0; i < maxChars; ++i)
188struct BufferedInputStreamTests final :
public UnitTest
190 template <
typename Fn,
size_t... Ix,
typename Values>
191 static void applyImpl (Fn&& fn, std::index_sequence<Ix...>, Values&& values)
193 fn (std::get<Ix> (values)...);
196 template <
typename Fn,
typename... Values>
197 static void apply (Fn&& fn, std::tuple<Values...> values)
199 applyImpl (fn, std::make_index_sequence<
sizeof... (Values)>(), values);
202 template <
typename Fn,
typename Values>
203 static void allCombinationsImpl (Fn&& fn, Values&& values)
208 template <
typename Fn,
typename Values,
typename Range,
typename... Ranges>
209 static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges)
211 for (
auto& item : range)
212 allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...);
215 template <
typename Fn,
typename... Ranges>
216 static void allCombinations (Fn&& fn, Ranges&&... ranges)
218 allCombinationsImpl (fn, std::tie(), ranges...);
221 BufferedInputStreamTests()
222 : UnitTest (
"BufferedInputStream", UnitTestCategories::streams)
225 void runTest()
override
227 const MemoryBlock testBufferA (
"abcdefghijklmnopqrstuvwxyz", 26);
229 const auto testBufferB = [&]
231 MemoryBlock mb { 8192 };
232 auto r = getRandom();
234 std::for_each (mb.begin(), mb.end(), [&] (
char& item)
236 item = (char) r.nextInt (std::numeric_limits<char>::max());
242 const MemoryBlock buffers[] { testBufferA, testBufferB };
243 const int readSizes[] { 3, 10, 50 };
244 const bool shouldPeek[] {
false,
true };
246 const auto runTest = [
this] (
const MemoryBlock& data,
const int readSize,
const bool peek)
248 MemoryInputStream mi (data,
true);
250 BufferedInputStream stream (mi, jmin (200, (
int) data.getSize()));
254 expectEquals (stream.getPosition(), (int64) 0);
255 expectEquals (stream.getTotalLength(), (int64) data.getSize());
256 expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
257 expect (! stream.isExhausted());
259 size_t numBytesRead = 0;
260 MemoryBlock readBuffer (data.getSize());
262 while (numBytesRead < data.getSize())
265 expectEquals (stream.peekByte(), *(
char*) (data.begin() + numBytesRead));
267 const auto startingPos = numBytesRead;
268 numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize);
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()));
279 expectEquals (stream.getPosition(), (int64) data.getSize());
280 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
281 expect (stream.isExhausted());
283 expect (readBuffer == data);
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());
294 const int numBytesToSkip = 5;
296 while (numBytesRead < data.getSize())
298 expectEquals (stream.peekByte(), *(
char*) (data.begin() + numBytesRead));
300 stream.skipNextBytes (numBytesToSkip);
301 numBytesRead += numBytesToSkip;
302 numBytesRead = std::min (numBytesRead, data.getSize());
304 expectEquals (stream.getPosition(), (int64) numBytesRead);
305 expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
306 expect (stream.isExhausted() == (numBytesRead == data.getSize()));
309 expectEquals (stream.getPosition(), (int64) data.getSize());
310 expectEquals (stream.getNumBytesRemaining(), (int64) 0);
311 expect (stream.isExhausted());
314 allCombinations (runTest, buffers, readSizes, shouldPeek);
318static BufferedInputStreamTests bufferedInputStreamTests;
constexpr ValueType getStart() const noexcept
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)