1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
/**********************************************************************
Audacity: A Digital Audio Editor
BufferedStreamReader.h
Dmitry Vedenko
**********************************************************************/
#pragma once
#include <vector>
#include <cstddef>
#include <cstdint>
#include <type_traits>
/*!
* \brief A facade-like class, that implements buffered reading from the underlying data stream.
*
* BufferedStreamReader provides optimal read performance for the built-in types of up to 8 bytes long.
*/
class UTILITY_API BufferedStreamReader /* not final */
{
public:
static constexpr size_t RequiredAlignment = 8;
explicit BufferedStreamReader(size_t bufferSize = 4096);
//! Read up to maxBytes into the buffer. Returns the number of bytes read.
size_t Read(void* buffer, size_t maxBytes);
//! Read a single value of ValueType, where sizeof(ValueType) <= 8 and value is aligned to the size boundary
template<typename ValueType>
std::enable_if_t<sizeof(ValueType) <= RequiredAlignment, bool>
ReadValue(ValueType& value) /* ReadData may throw */
{
constexpr size_t valueSize = sizeof(ValueType);
const size_t availableBytes = mCurrentBytes - mCurrentIndex;
// In case of underflow - just fall back to general Read routine
if (availableBytes < valueSize)
return valueSize == Read(&value, valueSize);
// Special case for one bytes reads
if constexpr (valueSize == 1)
value = mBufferStart[mCurrentIndex];
else
value = UncheckedRead<ValueType>();
mCurrentIndex += valueSize;
return true;
}
//! Returns true if there is no more data available
bool Eof() const;
//! Read a single byte from the stream. Return -1 on failure.
int GetC();
protected:
//! Should return true, if underlying stream has more data
virtual bool HasMoreData() const = 0;
//! Read up to maxBytes into the buffer. Should return the number of bytes read.
virtual size_t ReadData(void* buffer, size_t maxBytes) = 0;
private:
//! Reads the data from the stream if there is not enough data buffered
bool HandleUnderflow();
/*! Reads a value of type T from the stream.
* @pre mCurrentIndex + sizeof(T) <= mCurrentBytes
*/
template<typename T>
T UncheckedRead() noexcept
{
T result;
// We know, that mBufferStart is always at least RequiredAlignment aligned
// Only mCurrentIndex alignment matters here
if ((mCurrentIndex % sizeof(T)) == 0)
{
// If the result is aligned - just read it from the memory
const void* ptr = mBufferStart + mCurrentIndex;
result = *static_cast<const T*>(ptr);
}
else
{
// Fallback to std::copy otherwise
const uint8_t* begin = mBufferStart + mCurrentIndex;
const uint8_t* end = begin + sizeof(T);
void* out = &result;
std::copy(begin, end, static_cast<uint8_t*>(out));
}
return result;
}
// Cache storage
std::vector<uint8_t> mBufferData;
// mBufferStart is aligned to RequiredAlignment bytes boundary.
// Both values below are initialized in the constructor.
uint8_t* mBufferStart;
size_t mBufferSize;
size_t mCurrentIndex { 0 };
size_t mCurrentBytes { 0 };
};
|