Browse Source

Reservoir: Factor out reservoir management code

v6.1.6
reuk 3 years ago
parent
commit
930a3299f2
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
7 changed files with 334 additions and 204 deletions
  1. +44
    -47
      modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
  2. +41
    -51
      modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
  3. +1
    -0
      modules/juce_core/juce_core.h
  4. +100
    -0
      modules/juce_core/memory/juce_Reservoir.h
  5. +145
    -101
      modules/juce_core/streams/juce_BufferedInputStream.cpp
  6. +2
    -2
      modules/juce_core/streams/juce_BufferedInputStream.h
  7. +1
    -3
      modules/juce_core/streams/juce_MemoryInputStream.cpp

+ 44
- 47
modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp View File

@@ -167,6 +167,8 @@ namespace FlacNamespace
//==============================================================================
static const char* const flacFormatName = "FLAC file";
template <typename Item>
auto emptyRange (Item item) { return Range<Item>::emptyRange (item); }
//==============================================================================
class FlacReader : public AudioFormatReader
@@ -217,66 +219,61 @@ public:
reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true);
}
// returns the number of samples read
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) override
{
if (! ok)
return false;
while (numSamples > 0)
{
if (startSampleInFile >= reservoirStart
&& startSampleInFile < reservoirStart + samplesInReservoir)
{
auto num = (int) jmin ((int64) numSamples,
reservoirStart + samplesInReservoir - startSampleInFile);
jassert (num > 0);
const auto getBufferedRange = [this] { return bufferedRange; };
for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
if (destSamples[i] != nullptr)
memcpy (destSamples[i] + startOffsetInDestBuffer,
reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
(size_t) num * sizeof (int));
const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range<int64> rangeToRead)
{
const auto bufferIndices = rangeToRead - bufferedRange.getStart();
const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
startOffsetInDestBuffer += num;
startSampleInFile += num;
numSamples -= num;
}
else
for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
{
if (startSampleInFile >= lengthInSamples)
{
samplesInReservoir = 0;
}
else if (startSampleInFile < reservoirStart
|| startSampleInFile > reservoirStart + jmax (samplesInReservoir, (int64) 511))
{
// had some problems with flac crashing if the read pos is aligned more
// accurately than this. Probably fixed in newer versions of the library, though.
reservoirStart = (int) (startSampleInFile & ~511);
samplesInReservoir = 0;
FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) reservoirStart);
}
else
if (destSamples[i] != nullptr)
{
reservoirStart += samplesInReservoir;
samplesInReservoir = 0;
FLAC__stream_decoder_process_single (decoder);
memcpy (destSamples[i] + writePos,
reservoir.getReadPointer (i) + bufferIndices.getStart(),
(size_t) bufferIndices.getLength() * sizeof (int));
}
if (samplesInReservoir == 0)
break;
}
}
};
if (numSamples > 0)
const auto fillReservoir = [this] (const int64 requestedStart)
{
if (requestedStart >= lengthInSamples)
{
bufferedRange = emptyRange (requestedStart);
return;
}
if (requestedStart < bufferedRange.getStart()
|| jmax (bufferedRange.getEnd(), bufferedRange.getStart() + (int64) 511) < requestedStart)
{
// had some problems with flac crashing if the read pos is aligned more
// accurately than this. Probably fixed in newer versions of the library, though.
bufferedRange = emptyRange (requestedStart & ~511);
FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) bufferedRange.getStart());
return;
}
bufferedRange = emptyRange (bufferedRange.getEnd());
FLAC__stream_decoder_process_single (decoder);
};
const auto remainingSamples = Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
getBufferedRange,
readFromReservoir,
fillReservoir);
if (! remainingSamples.isEmpty())
for (int i = numDestChannels; --i >= 0;)
if (destSamples[i] != nullptr)
zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
}
zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) remainingSamples.getLength() * sizeof (int));
return true;
}
@@ -304,14 +301,14 @@ public:
if (src != nullptr)
{
auto* dest = reinterpret_cast<int*> (reservoir.getWritePointer(i));
auto* dest = reinterpret_cast<int*> (reservoir.getWritePointer (i));
for (int j = 0; j < numSamples; ++j)
dest[j] = src[j] << bitsToShift;
}
}
samplesInReservoir = numSamples;
bufferedRange.setLength (numSamples);
}
}
@@ -368,7 +365,7 @@ public:
private:
FlacNamespace::FLAC__StreamDecoder* decoder;
AudioBuffer<float> reservoir;
int64 reservoirStart = 0, samplesInReservoir = 0;
Range<int64> bufferedRange;
bool ok = false, scanningForLength = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader)


+ 41
- 51
modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp View File

@@ -158,72 +158,62 @@ public:
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) override
{
while (numSamples > 0)
const auto getBufferedRange = [this] { return bufferedRange; };
const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range<int64> rangeToRead)
{
auto numAvailable = (reservoirStart + samplesInReservoir - startSampleInFile);
const auto bufferIndices = rangeToRead - bufferedRange.getStart();
const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile);
if (startSampleInFile >= reservoirStart && numAvailable > 0)
{
// got a few samples overlapping, so use them before seeking..
for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
if (destSamples[i] != nullptr)
memcpy (destSamples[i] + writePos,
reservoir.getReadPointer (i) + bufferIndices.getStart(),
(size_t) bufferIndices.getLength() * sizeof (float));
};
auto numToUse = jmin ((int64) numSamples, numAvailable);
const auto fillReservoir = [this] (int64 requestedStart)
{
const auto newStart = jmax ((int64) 0, requestedStart);
bufferedRange = Range<int64> { newStart, newStart + reservoir.getNumSamples() };
for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
if (destSamples[i] != nullptr)
memcpy (destSamples[i] + startOffsetInDestBuffer,
reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)),
(size_t) numToUse * sizeof (float));
if (bufferedRange.getStart() != ov_pcm_tell (&ovFile))
ov_pcm_seek (&ovFile, bufferedRange.getStart());
startSampleInFile += numToUse;
numSamples -= (int) numToUse;
startOffsetInDestBuffer += (int) numToUse;
int bitStream = 0;
int offset = 0;
int numToRead = (int) bufferedRange.getLength();
if (numSamples == 0)
break;
}
if (startSampleInFile < reservoirStart
|| startSampleInFile + numSamples > reservoirStart + samplesInReservoir)
while (numToRead > 0)
{
// buffer miss, so refill the reservoir
reservoirStart = jmax (0, (int) startSampleInFile);
samplesInReservoir = reservoir.getNumSamples();
if (reservoirStart != (int) ov_pcm_tell (&ovFile))
ov_pcm_seek (&ovFile, reservoirStart);
int bitStream = 0;
int offset = 0;
int numToRead = (int) samplesInReservoir;
float** dataIn = nullptr;
auto samps = static_cast<int> (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream));
while (numToRead > 0)
{
float** dataIn = nullptr;
auto samps = static_cast<int> (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream));
if (samps <= 0)
break;
if (samps <= 0)
break;
jassert (samps <= numToRead);
jassert (samps <= numToRead);
for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float));
for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;)
memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float));
numToRead -= samps;
offset += samps;
}
numToRead -= samps;
offset += samps;
}
if (numToRead > 0)
reservoir.clear (offset, numToRead);
};
if (numToRead > 0)
reservoir.clear (offset, numToRead);
}
}
const auto remainingSamples = Reservoir::doBufferedRead (Range<int64> { startSampleInFile, startSampleInFile + numSamples },
getBufferedRange,
readFromReservoir,
fillReservoir);
if (numSamples > 0)
{
if (! remainingSamples.isEmpty())
for (int i = numDestChannels; --i >= 0;)
if (destSamples[i] != nullptr)
zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int));
}
zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) remainingSamples.getLength() * sizeof (int));
return true;
}
@@ -261,7 +251,7 @@ private:
OggVorbisNamespace::OggVorbis_File ovFile;
OggVorbisNamespace::ov_callbacks callbacks;
AudioBuffer<float> reservoir;
int64 reservoirStart = 0, samplesInReservoir = 0;
Range<int64> bufferedRange;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
};


+ 1
- 0
modules/juce_core/juce_core.h View File

@@ -341,6 +341,7 @@ JUCE_END_IGNORE_WARNINGS_MSVC
#include "containers/juce_PropertySet.h"
#include "memory/juce_SharedResourcePointer.h"
#include "memory/juce_AllocationHooks.h"
#include "memory/juce_Reservoir.h"
#if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS)
#include "native/juce_mac_ObjCHelpers.h"


+ 100
- 0
modules/juce_core/memory/juce_Reservoir.h View File

@@ -0,0 +1,100 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Helper functions for managing buffered readers.
*/
struct Reservoir
{
/** Attempts to read the requested range from some kind of input stream,
with intermediate buffering in a 'reservoir'.
While there are still samples in the requested range left to read, this
function will check whether the next part of the requested range is
already loaded into the reservoir. If the range is available, then
doBufferedRead will call readFromReservoir with the range that should
be copied to the output buffer. If the range is not available,
doBufferedRead will call fillReservoir to request that a new region is
loaded into the reservoir. It will repeat these steps until either the
entire requested region has been read, or the stream ends.
This will return the range that could not be read successfully, if any.
An empty range implies that the entire read was successful.
Note that all ranges, including those provided as arguments to the
callbacks, are relative to the original unbuffered input. That is, if
getBufferedRange returns the range [200, 300), then readFromReservoir
might be passed the range [250, 300) in order to copy the final 50
samples from the reservoir.
@param rangeToRead the absolute position of the range that should
be read
@param getBufferedRange a function void -> Range<Index> that returns
the region currently held in the reservoir
@param readFromReservoir a function Range<Index> -> void that can be
used to copy samples from the region in the
reservoir specified in the input range
@param fillReservoir a function Index -> void that is given a
requested read location, and that should
attempt to fill the reservoir starting at this
location. After this function,
getBufferedRange should return the new region
contained in the managed buffer
*/
template <typename Index, typename GetBufferedRange, typename ReadFromReservoir, typename FillReservoir>
static Range<Index> doBufferedRead (Range<Index> rangeToRead,
GetBufferedRange&& getBufferedRange,
ReadFromReservoir&& readFromReservoir,
FillReservoir&& fillReservoir)
{
while (! rangeToRead.isEmpty())
{
const auto rangeToReadInBuffer = rangeToRead.getIntersectionWith (getBufferedRange());
if (rangeToReadInBuffer.isEmpty())
{
fillReservoir (rangeToRead.getStart());
const auto newRange = getBufferedRange();
if (newRange.isEmpty() || ! newRange.contains (rangeToRead.getStart()))
break;
}
else
{
readFromReservoir (rangeToReadInBuffer);
rangeToRead.setStart (rangeToReadInBuffer.getEnd());
}
}
return rangeToRead;
}
};
} // namespace juce

+ 145
- 101
modules/juce_core/streams/juce_BufferedInputStream.cpp View File

@@ -39,22 +39,20 @@ static int calcBufferStreamBufferSize (int requestedSize, InputStream* source) n
//==============================================================================
BufferedInputStream::BufferedInputStream (InputStream* sourceStream, int size, bool takeOwnership)
: source (sourceStream, takeOwnership),
bufferSize (calcBufferStreamBufferSize (size, sourceStream)),
position (sourceStream->getPosition()),
bufferStart (position)
: source (sourceStream, takeOwnership),
bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()),
position (bufferedRange.getStart()),
bufferLength (calcBufferStreamBufferSize (size, sourceStream))
{
buffer.malloc (bufferSize);
buffer.malloc (bufferLength);
}
BufferedInputStream::BufferedInputStream (InputStream& sourceStream, int size)
: BufferedInputStream (&sourceStream, size, false)
: BufferedInputStream (&sourceStream, size, false)
{
}
BufferedInputStream::~BufferedInputStream()
{
}
BufferedInputStream::~BufferedInputStream() = default;
//==============================================================================
char BufferedInputStream::peekByte()
@@ -62,7 +60,7 @@ char BufferedInputStream::peekByte()
if (! ensureBuffered())
return 0;
return position < lastReadPos ? buffer[(int) (position - bufferStart)] : 0;
return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0;
}
int64 BufferedInputStream::getTotalLength()
@@ -90,20 +88,19 @@ bool BufferedInputStream::ensureBuffered()
{
auto bufferEndOverlap = lastReadPos - bufferOverlap;
if (position < bufferStart || position >= bufferEndOverlap)
if (position < bufferedRange.getStart() || position >= bufferEndOverlap)
{
int bytesRead;
int bytesRead = 0;
if (position < lastReadPos
&& position >= bufferEndOverlap
&& position >= bufferStart)
&& position >= bufferedRange.getStart())
{
auto bytesToKeep = (int) (lastReadPos - position);
memmove (buffer, buffer + (int) (position - bufferStart), (size_t) bytesToKeep);
memmove (buffer, buffer + (int) (position - bufferedRange.getStart()), (size_t) bytesToKeep);
bufferStart = position;
bytesRead = source->read (buffer + bytesToKeep,
(int) (bufferSize - bytesToKeep));
(int) (bufferLength - bytesToKeep));
if (bytesRead < 0)
return false;
@@ -113,75 +110,62 @@ bool BufferedInputStream::ensureBuffered()
}
else
{
bufferStart = position;
if (! source->setPosition (bufferStart))
if (! source->setPosition (position))
return false;
bytesRead = source->read (buffer, bufferSize);
bytesRead = (int) source->read (buffer, (size_t) bufferLength);
if (bytesRead < 0)
return false;
lastReadPos = bufferStart + bytesRead;
lastReadPos = position + bytesRead;
}
while (bytesRead < bufferSize)
bufferedRange = Range<int64> (position, lastReadPos);
while (bytesRead < bufferLength)
buffer[bytesRead++] = 0;
}
return true;
}
int BufferedInputStream::read (void* destBuffer, int maxBytesToRead)
int BufferedInputStream::read (void* destBuffer, const int maxBytesToRead)
{
jassert (destBuffer != nullptr && maxBytesToRead >= 0);
const auto initialPosition = position;
if (position >= bufferStart
&& position + maxBytesToRead <= lastReadPos)
{
memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) maxBytesToRead);
position += maxBytesToRead;
return maxBytesToRead;
}
const auto getBufferedRange = [this] { return bufferedRange; };
if (position < bufferStart || position >= lastReadPos)
if (! ensureBuffered())
return 0;
int bytesRead = 0;
while (maxBytesToRead > 0)
const auto readFromReservoir = [this, &destBuffer, &initialPosition] (const Range<int64> rangeToRead)
{
auto numToRead = jmin (maxBytesToRead, (int) (lastReadPos - position));
if (numToRead > 0)
{
memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) numToRead);
maxBytesToRead -= numToRead;
bytesRead += numToRead;
position += numToRead;
destBuffer = static_cast<char*> (destBuffer) + numToRead;
}
memcpy (static_cast<char*> (destBuffer) + (rangeToRead.getStart() - initialPosition),
buffer + (rangeToRead.getStart() - bufferedRange.getStart()),
(size_t) rangeToRead.getLength());
};
auto oldLastReadPos = lastReadPos;
if (! ensureBuffered()
|| oldLastReadPos == lastReadPos
|| isExhausted())
break;
}
return bytesRead;
const auto fillReservoir = [this] (int64 requestedStart)
{
position = requestedStart;
ensureBuffered();
};
const auto remaining = Reservoir::doBufferedRead (Range<int64> (position, position + maxBytesToRead),
getBufferedRange,
readFromReservoir,
fillReservoir);
const auto bytesRead = maxBytesToRead - remaining.getLength();
position = remaining.getStart();
return (int) bytesRead;
}
String BufferedInputStream::readString()
{
if (position >= bufferStart
if (position >= bufferedRange.getStart()
&& position < lastReadPos)
{
auto maxChars = (int) (lastReadPos - position);
auto* src = buffer + (int) (position - bufferStart);
auto* src = buffer + (int) (position - bufferedRange.getStart());
for (int i = 0; i < maxChars; ++i)
{
@@ -203,71 +187,131 @@ String BufferedInputStream::readString()
struct BufferedInputStreamTests : public UnitTest
{
template <typename Fn, size_t... Ix, typename Values>
static void applyImpl (Fn&& fn, std::index_sequence<Ix...>, Values&& values)
{
fn (std::get<Ix> (values)...);
}
template <typename Fn, typename... Values>
static void apply (Fn&& fn, std::tuple<Values...> values)
{
applyImpl (fn, std::make_index_sequence<sizeof... (Values)>(), values);
}
template <typename Fn, typename Values>
static void allCombinationsImpl (Fn&& fn, Values&& values)
{
apply (fn, values);
}
template <typename Fn, typename Values, typename Range, typename... Ranges>
static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges)
{
for (auto& item : range)
allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...);
}
template <typename Fn, typename... Ranges>
static void allCombinations (Fn&& fn, Ranges&&... ranges)
{
allCombinationsImpl (fn, std::tie(), ranges...);
}
BufferedInputStreamTests()
: UnitTest ("BufferedInputStream", UnitTestCategories::streams)
{}
void runTest() override
{
const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26);
MemoryInputStream mi (data, true);
const MemoryBlock testBufferA ("abcdefghijklmnopqrstuvwxyz", 26);
BufferedInputStream stream (mi, (int) data.getSize());
const auto testBufferB = [&]
{
MemoryBlock mb { 8192 };
auto r = getRandom();
beginTest ("Read");
std::for_each (mb.begin(), mb.end(), [&] (char& item)
{
item = (char) r.nextInt (std::numeric_limits<char>::max());
});
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
return mb;
}();
size_t numBytesRead = 0;
MemoryBlock readBuffer (data.getSize());
const MemoryBlock buffers[] { testBufferA, testBufferB };
const int readSizes[] { 3, 10, 50 };
const bool shouldPeek[] { false, true };
while (numBytesRead < data.getSize())
const auto runTest = [this] (const MemoryBlock& data, const int readSize, const bool peek)
{
expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
MemoryInputStream mi (data, true);
numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3);
BufferedInputStream stream (mi, jmin (200, (int) data.getSize()));
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
beginTest ("Read");
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
expect (readBuffer == data);
size_t numBytesRead = 0;
MemoryBlock readBuffer (data.getSize());
beginTest ("Skip");
while (numBytesRead < data.getSize())
{
if (peek)
expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
const auto startingPos = numBytesRead;
numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize);
expect (std::equal (readBuffer.begin() + startingPos,
readBuffer.begin() + numBytesRead,
data.begin() + startingPos,
data.begin() + numBytesRead));
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
stream.setPosition (0);
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
numBytesRead = 0;
const int numBytesToSkip = 5;
expect (readBuffer == data);
while (numBytesRead < data.getSize())
{
expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
beginTest ("Skip");
stream.skipNextBytes (numBytesToSkip);
numBytesRead += numBytesToSkip;
numBytesRead = std::min (numBytesRead, data.getSize());
stream.setPosition (0);
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
numBytesRead = 0;
const int numBytesToSkip = 5;
while (numBytesRead < data.getSize())
{
expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead));
stream.skipNextBytes (numBytesToSkip);
numBytesRead += numBytesToSkip;
numBytesRead = std::min (numBytesRead, data.getSize());
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
};
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
allCombinations (runTest, buffers, readSizes, shouldPeek);
}
};


+ 2
- 2
modules/juce_core/streams/juce_BufferedInputStream.h View File

@@ -79,8 +79,8 @@ public:
private:
//==============================================================================
OptionalScopedPointer<InputStream> source;
int bufferSize;
int64 position, lastReadPos = 0, bufferStart, bufferOverlap = 128;
Range<int64> bufferedRange;
int64 position, bufferLength, lastReadPos = 0, bufferOverlap = 128;
HeapBlock<char> buffer;
bool ensureBuffered();


+ 1
- 3
modules/juce_core/streams/juce_MemoryInputStream.cpp View File

@@ -52,9 +52,7 @@ MemoryInputStream::MemoryInputStream (MemoryBlock&& source)
dataSize = internalCopy.getSize();
}
MemoryInputStream::~MemoryInputStream()
{
}
MemoryInputStream::~MemoryInputStream() = default;
int64 MemoryInputStream::getTotalLength()
{


Loading…
Cancel
Save