Do some cleanup in the Buffer class
This commit is contained in:
parent
364a2afb3e
commit
43a7df2de3
3 changed files with 119 additions and 84 deletions
|
|
@ -64,12 +64,12 @@ public:
|
||||||
* @return true if the resize worked
|
* @return true if the resize worked
|
||||||
* @return false otherwise
|
* @return false otherwise
|
||||||
*/
|
*/
|
||||||
bool resize(size_t newSize)
|
bool resize(size_t newSize, std::nothrow_t) noexcept
|
||||||
{
|
{
|
||||||
bool returnedOK = true;
|
bool returnedOK = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < numChannels; ++i)
|
for (size_t i = 0; i < numChannels; ++i)
|
||||||
returnedOK &= buffers[i]->resize(newSize);
|
returnedOK &= buffers[i]->resize(newSize, std::nothrow);
|
||||||
|
|
||||||
if (returnedOK)
|
if (returnedOK)
|
||||||
numFrames = newSize;
|
numFrames = newSize;
|
||||||
|
|
@ -77,6 +77,19 @@ public:
|
||||||
return returnedOK;
|
return returnedOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resizes all the underlying buffers to a new size.
|
||||||
|
*
|
||||||
|
* @param newSize
|
||||||
|
*/
|
||||||
|
void resize(size_t newSize)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < numChannels; ++i)
|
||||||
|
buffers[i]->resize(newSize);
|
||||||
|
|
||||||
|
numFrames = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return an iterator to a specific channel with a non-const type.
|
* @brief Return an iterator to a specific channel with a non-const type.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ namespace sfz
|
||||||
class BufferCounter
|
class BufferCounter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BufferCounter() = default;
|
BufferCounter() noexcept = default;
|
||||||
~BufferCounter()
|
~BufferCounter() noexcept
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::cout << "Remaining buffers on destruction: " << numBuffers << '\n';
|
std::cout << "Remaining buffers on destruction: " << numBuffers << '\n';
|
||||||
|
|
@ -54,41 +54,41 @@ public:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void newBuffer(int size)
|
void newBuffer(int size) noexcept
|
||||||
{
|
{
|
||||||
numBuffers++;
|
numBuffers++;
|
||||||
bytes.fetch_add(size);
|
bytes.fetch_add(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bufferResized(int oldSize, int newSize)
|
void bufferResized(int oldSize, int newSize) noexcept
|
||||||
{
|
{
|
||||||
bytes.fetch_add(newSize);
|
bytes.fetch_add(newSize);
|
||||||
bytes.fetch_sub(oldSize);
|
bytes.fetch_sub(oldSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bufferDeleted(int size)
|
void bufferDeleted(int size) noexcept
|
||||||
{
|
{
|
||||||
numBuffers--;
|
numBuffers--;
|
||||||
bytes.fetch_sub(size);
|
bytes.fetch_sub(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bufferDeleted(size_t size)
|
void bufferDeleted(size_t size) noexcept
|
||||||
{
|
{
|
||||||
bufferDeleted(static_cast<int>(size));
|
bufferDeleted(static_cast<int>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void bufferResized(size_t oldSize, size_t newSize)
|
void bufferResized(size_t oldSize, size_t newSize) noexcept
|
||||||
{
|
{
|
||||||
bufferResized(static_cast<int>(oldSize), static_cast<int>(newSize));
|
bufferResized(static_cast<int>(oldSize), static_cast<int>(newSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
void newBuffer(size_t size)
|
void newBuffer(size_t size) noexcept
|
||||||
{
|
{
|
||||||
newBuffer(static_cast<int>(size));
|
newBuffer(static_cast<int>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNumBuffers() { return numBuffers; }
|
int getNumBuffers() const noexcept { return numBuffers; }
|
||||||
int getTotalBytes() { return bytes; }
|
int getTotalBytes() const noexcept { return bytes; }
|
||||||
private:
|
private:
|
||||||
std::atomic<int> numBuffers { 0 };
|
std::atomic<int> numBuffers { 0 };
|
||||||
std::atomic<int> bytes { 0 };
|
std::atomic<int> bytes { 0 };
|
||||||
|
|
@ -128,9 +128,8 @@ public:
|
||||||
* @brief Construct a new Buffer object that is empty
|
* @brief Construct a new Buffer object that is empty
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Buffer()
|
Buffer() noexcept
|
||||||
{
|
{
|
||||||
counter().newBuffer(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -138,9 +137,8 @@ public:
|
||||||
*
|
*
|
||||||
* @param size
|
* @param size
|
||||||
*/
|
*/
|
||||||
Buffer(size_t size)
|
explicit Buffer(size_t size)
|
||||||
{
|
{
|
||||||
counter().newBuffer(0);
|
|
||||||
resize(size);
|
resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +150,7 @@ public:
|
||||||
* @return true if allocation succeeded
|
* @return true if allocation succeeded
|
||||||
* @return false otherwise
|
* @return false otherwise
|
||||||
*/
|
*/
|
||||||
bool resize(size_t newSize)
|
bool resize(size_t newSize, std::nothrow_t) noexcept
|
||||||
{
|
{
|
||||||
if (newSize == 0) {
|
if (newSize == 0) {
|
||||||
clear();
|
clear();
|
||||||
|
|
@ -160,19 +158,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end
|
auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end
|
||||||
auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type));
|
auto* newData = std::realloc(paddedData.get(), tempSize * sizeof(value_type));
|
||||||
if (newData == nullptr) {
|
if (newData == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
counter().bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type));
|
if (largerSize > 0)
|
||||||
|
_counter.bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type));
|
||||||
|
else
|
||||||
|
_counter.newBuffer(tempSize * sizeof(value_type));
|
||||||
|
|
||||||
largerSize = tempSize;
|
largerSize = tempSize;
|
||||||
alignedSize = newSize;
|
alignedSize = newSize;
|
||||||
paddedData = static_cast<pointer>(newData);
|
paddedData.release(); // realloc has invalidated the old pointer
|
||||||
|
paddedData.reset(static_cast<pointer>(newData));
|
||||||
normalData = static_cast<pointer>(align(Alignment, alignedSize, newData, tempSize));
|
normalData = static_cast<pointer>(align(Alignment, alignedSize, newData, tempSize));
|
||||||
normalEnd = normalData + alignedSize;
|
normalEnd = normalData + alignedSize;
|
||||||
auto endMisalignment = (alignedSize & TypeAlignmentMask);
|
auto endMisalignment = (alignedSize & TypeAlignmentMask);
|
||||||
if (endMisalignment != 0)
|
if (endMisalignment != 0)
|
||||||
_alignedEnd = normalEnd + Alignment - endMisalignment;
|
_alignedEnd = normalEnd + Alignment - endMisalignment;
|
||||||
else
|
else
|
||||||
_alignedEnd = normalEnd;
|
_alignedEnd = normalEnd;
|
||||||
|
|
@ -180,24 +183,32 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resizes the buffer. Given that std::realloc may return either the same pointer
|
||||||
|
* or a new one, you need to account for both cases in the code if you are using deep pointers.
|
||||||
|
*
|
||||||
|
* @param newSize the new buffer size in bytes
|
||||||
|
*/
|
||||||
|
void resize(size_t newSize)
|
||||||
|
{
|
||||||
|
if (!resize(newSize, std::nothrow))
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clear the buffers and frees the underlying memory
|
* @brief Clear the buffers and frees the underlying memory
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void clear()
|
void clear() noexcept
|
||||||
{
|
{
|
||||||
counter().bufferResized(largerSize * sizeof(value_type), static_cast<size_t>(0));
|
if (largerSize > 0)
|
||||||
largerSize = 0;
|
_counter.bufferDeleted(largerSize * sizeof(value_type));
|
||||||
alignedSize = 0;
|
_clear();
|
||||||
std::free(paddedData);
|
|
||||||
normalData = nullptr;
|
|
||||||
normalEnd = nullptr;
|
|
||||||
_alignedEnd = nullptr;
|
|
||||||
}
|
}
|
||||||
~Buffer()
|
~Buffer() noexcept
|
||||||
{
|
{
|
||||||
counter().bufferDeleted(largerSize * sizeof(value_type));
|
if (largerSize > 0)
|
||||||
std::free(paddedData);
|
_counter.bufferDeleted(largerSize * sizeof(value_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -207,10 +218,8 @@ public:
|
||||||
*/
|
*/
|
||||||
Buffer(const Buffer<Type>& other)
|
Buffer(const Buffer<Type>& other)
|
||||||
{
|
{
|
||||||
counter().newBuffer(0);
|
resize(other.size());
|
||||||
if (resize(other.size())) {
|
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
|
||||||
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -218,45 +227,41 @@ public:
|
||||||
*
|
*
|
||||||
* @param other
|
* @param other
|
||||||
*/
|
*/
|
||||||
Buffer(Buffer<Type>&& other) = delete;
|
Buffer(Buffer<Type>&& other) noexcept
|
||||||
// {
|
: largerSize(other.largerSize),
|
||||||
// if (this != &other) {
|
alignedSize(other.alignedSize),
|
||||||
// counter().bufferDeleted(largerSize * sizeof(value_type));
|
normalData(other.normalData),
|
||||||
// std::free(paddedData);
|
paddedData(std::move(other.paddedData)),
|
||||||
// largerSize = std::exchange(other.largerSize, 0);
|
normalEnd(other.normalEnd),
|
||||||
// alignedSize = std::exchange(other.alignedSize, 0);
|
_alignedEnd(other._alignedEnd)
|
||||||
// paddedData = std::exchange(other.paddedData, nullptr);
|
{
|
||||||
// normalData = std::exchange(other.normalData, nullptr);
|
other._clear();
|
||||||
// normalEnd = std::exchange(other.normalEnd, nullptr);
|
}
|
||||||
// _alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Buffer<Type>& operator=(const Buffer<Type>& other)
|
Buffer<Type>& operator=(const Buffer<Type>& other)
|
||||||
{
|
{
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
if (resize(other.size()))
|
resize(other.size());
|
||||||
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
|
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer<Type>& operator=(Buffer<Type>&& other) = delete;
|
Buffer<Type>& operator=(Buffer<Type>&& other) noexcept
|
||||||
// {
|
{
|
||||||
// if (this != &other) {
|
if (this != &other) {
|
||||||
// counter().bufferDeleted(largerSize * sizeof(value_type));
|
largerSize = other.largerSize;
|
||||||
// std::free(paddedData);
|
alignedSize = other.alignedSize;
|
||||||
// largerSize = std::exchange(other.largerSize, 0);
|
normalData = other.normalData;
|
||||||
// alignedSize = std::exchange(other.alignedSize, 0);
|
paddedData = std::move(other.paddedData);
|
||||||
// paddedData = std::exchange(other.paddedData, nullptr);
|
normalEnd = other.normalEnd;
|
||||||
// normalData = std::exchange(other.normalData, nullptr);
|
_alignedEnd = other._alignedEnd;
|
||||||
// normalEnd = std::exchange(other.normalEnd, nullptr);
|
other._clear();
|
||||||
// _alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
}
|
||||||
// }
|
return *this;
|
||||||
// return *this;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
Type& operator[](size_t idx) { return *(normalData + idx); }
|
Type& operator[](size_t idx) noexcept { return *(normalData + idx); }
|
||||||
constexpr pointer data() const noexcept { return normalData; }
|
constexpr pointer data() const noexcept { return normalData; }
|
||||||
constexpr size_type size() const noexcept { return alignedSize; }
|
constexpr size_type size() const noexcept { return alignedSize; }
|
||||||
constexpr bool empty() const noexcept { return alignedSize == 0; }
|
constexpr bool empty() const noexcept { return alignedSize == 0; }
|
||||||
|
|
@ -264,19 +269,27 @@ public:
|
||||||
constexpr iterator end() const noexcept { return normalEnd; }
|
constexpr iterator end() const noexcept { return normalEnd; }
|
||||||
constexpr pointer alignedEnd() const noexcept { return _alignedEnd; }
|
constexpr pointer alignedEnd() const noexcept { return _alignedEnd; }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the buffer counter object.
|
* @brief Return the buffer counter object.
|
||||||
*
|
*
|
||||||
* TODO: In C++ 17 all of this can be static inline.
|
|
||||||
*
|
|
||||||
* @return The buffer counter on which you can call various methods.
|
* @return The buffer counter on which you can call various methods.
|
||||||
*/
|
*/
|
||||||
static BufferCounter& counter()
|
static BufferCounter& counter() noexcept
|
||||||
{
|
{
|
||||||
static BufferCounter counter;
|
return _counter;
|
||||||
return counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _clear()
|
||||||
|
{
|
||||||
|
largerSize = 0;
|
||||||
|
alignedSize = 0;
|
||||||
|
paddedData.reset();
|
||||||
|
normalData = nullptr;
|
||||||
|
normalEnd = nullptr;
|
||||||
|
_alignedEnd = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int AlignmentMask { Alignment - 1 };
|
static constexpr int AlignmentMask { Alignment - 1 };
|
||||||
static constexpr int TypeAlignment { Alignment / sizeof(value_type) };
|
static constexpr int TypeAlignment { Alignment / sizeof(value_type) };
|
||||||
|
|
@ -295,13 +308,22 @@ private:
|
||||||
return ptr = reinterpret_cast< void * >( aligned );
|
return ptr = reinterpret_cast< void * >( aligned );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct deleter {
|
||||||
|
void operator()(void *p) const noexcept { std::free(p); }
|
||||||
|
};
|
||||||
|
|
||||||
size_type largerSize { 0 };
|
size_type largerSize { 0 };
|
||||||
size_type alignedSize { 0 };
|
size_type alignedSize { 0 };
|
||||||
pointer normalData { nullptr };
|
pointer normalData { nullptr };
|
||||||
pointer paddedData { nullptr };
|
std::unique_ptr<value_type[], deleter> paddedData;
|
||||||
pointer normalEnd { nullptr };
|
pointer normalEnd { nullptr };
|
||||||
pointer _alignedEnd { nullptr };
|
pointer _alignedEnd { nullptr };
|
||||||
|
static BufferCounter _counter;
|
||||||
|
|
||||||
LEAK_DETECTOR(Buffer);
|
LEAK_DETECTOR(Buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class Type, unsigned int Alignment>
|
||||||
|
BufferCounter Buffer<Type, Alignment>::_counter;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,12 @@ TEST_CASE("[Buffer] Resize 10 floats ")
|
||||||
|
|
||||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||||
|
|
||||||
REQUIRE(buffer.resize(smallSize));
|
REQUIRE(buffer.resize(smallSize, std::nothrow));
|
||||||
checkBoundaries(buffer, smallSize);
|
checkBoundaries(buffer, smallSize);
|
||||||
|
|
||||||
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
||||||
|
|
||||||
REQUIRE(buffer.resize(bigSize));
|
REQUIRE(buffer.resize(bigSize, std::nothrow));
|
||||||
checkBoundaries(buffer, bigSize);
|
checkBoundaries(buffer, bigSize);
|
||||||
for (auto i = 0; i < smallSize; ++i)
|
for (auto i = 0; i < smallSize; ++i)
|
||||||
REQUIRE(buffer[i] == 1.0f);
|
REQUIRE(buffer[i] == 1.0f);
|
||||||
|
|
@ -92,12 +92,12 @@ TEST_CASE("[Buffer] Resize 4096 floats ")
|
||||||
|
|
||||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||||
|
|
||||||
REQUIRE(buffer.resize(smallSize));
|
REQUIRE(buffer.resize(smallSize, std::nothrow));
|
||||||
checkBoundaries(buffer, smallSize);
|
checkBoundaries(buffer, smallSize);
|
||||||
|
|
||||||
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
||||||
|
|
||||||
REQUIRE(buffer.resize(bigSize));
|
REQUIRE(buffer.resize(bigSize, std::nothrow));
|
||||||
checkBoundaries(buffer, bigSize);
|
checkBoundaries(buffer, bigSize);
|
||||||
for (auto i = 0; i < smallSize; ++i)
|
for (auto i = 0; i < smallSize; ++i)
|
||||||
REQUIRE(buffer[i] == 1.0f);
|
REQUIRE(buffer[i] == 1.0f);
|
||||||
|
|
@ -114,12 +114,12 @@ TEST_CASE("[Buffer] Resize 65536 floats ")
|
||||||
|
|
||||||
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
std::fill(buffer.begin(), buffer.end(), 1.0f);
|
||||||
|
|
||||||
REQUIRE(buffer.resize(smallSize));
|
REQUIRE(buffer.resize(smallSize, std::nothrow));
|
||||||
checkBoundaries(buffer, smallSize);
|
checkBoundaries(buffer, smallSize);
|
||||||
|
|
||||||
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](float value) { return value == 1.0f; }));
|
||||||
|
|
||||||
REQUIRE(buffer.resize(bigSize));
|
REQUIRE(buffer.resize(bigSize, std::nothrow));
|
||||||
checkBoundaries(buffer, bigSize);
|
checkBoundaries(buffer, bigSize);
|
||||||
for (auto i = 0; i < smallSize; ++i)
|
for (auto i = 0; i < smallSize; ++i)
|
||||||
REQUIRE(buffer[i] == 1.0f);
|
REQUIRE(buffer[i] == 1.0f);
|
||||||
|
|
@ -140,8 +140,8 @@ TEST_CASE("[Buffer] Copy and move")
|
||||||
checkBoundaries(copyConstructed, baseSize);
|
checkBoundaries(copyConstructed, baseSize);
|
||||||
REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](float value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](float value) { return value == 1.0f; }));
|
||||||
|
|
||||||
// sfz::Buffer<float> moveConstructed { std::move(buffer) };
|
sfz::Buffer<float> moveConstructed { std::move(buffer) };
|
||||||
// REQUIRE(buffer.empty());
|
REQUIRE(buffer.empty());
|
||||||
// checkBoundaries(moveConstructed, baseSize);
|
checkBoundaries(moveConstructed, baseSize);
|
||||||
// REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](float value) { return value == 1.0f; }));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue