26static int calcBufferStreamBufferSize (
int requestedSize, InputStream* source)
noexcept
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),
43 bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()),
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();
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>
196 template <
typename Fn,
typename... 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)
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();
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;
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;
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory.
A general-purpose range object, that simply represents any linear range with a start and end point.
constexpr ValueType getStart() const noexcept
Returns the start of the range.
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.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
long long int64
A platform-independent 64-bit integer type.
static Range< Index > doBufferedRead(Range< Index > rangeToRead, GetBufferedRange &&getBufferedRange, ReadFromReservoir &&readFromReservoir, FillReservoir &&fillReservoir)
Attempts to read the requested range from some kind of input stream, with intermediate buffering in a...