diff --git a/sources/Buffer.h b/sources/Buffer.h index 2104c4da..5c6fc527 100644 --- a/sources/Buffer.h +++ b/sources/Buffer.h @@ -1,18 +1,20 @@ #pragma once #include "Globals.h" -#include #include +#include #include +#include +#include -template +template class Buffer { public: using value_type = std::remove_cv_t; - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; @@ -20,7 +22,9 @@ public: using size_type = size_t; using difference_type = ptrdiff_t; - constexpr Buffer() { } + Buffer() + { + } Buffer(size_t size) { resize(size); @@ -34,9 +38,11 @@ public: } 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 = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type)); if (newData == nullptr) + { return false; + } largerSize = tempSize; alignedSize = newSize; @@ -47,6 +53,8 @@ public: _alignedEnd = normalEnd + Alignment - endMisalignment; else _alignedEnd = normalEnd; + + DBG("Buffer resized (" << newSize << ") at: " << this); return true; } @@ -64,24 +72,68 @@ public: std::free(paddedData); } - Type& operator[](int idx) { return *(normalData + idx); } + Buffer(const Buffer &other) + { + if (resize(other.size())) + { + std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); + } + } + + Buffer(Buffer &&other) + { + largerSize = std::exchange(other.largerSize, 0); + alignedSize = std::exchange(other.alignedSize, 0); + paddedData = std::exchange(other.paddedData, nullptr); + normalData = std::exchange(other.normalData, nullptr); + normalEnd = std::exchange(other.normalEnd, nullptr); + _alignedEnd = std::exchange(other._alignedEnd, nullptr); + } + + Buffer &operator=(const Buffer &other) + { + if (this != &other) + { + if (resize(other.size())) + std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); + } + return *this; + } + + Buffer &operator=(Buffer &&other) + { + if (this != &other) + { + std::free(paddedData); + largerSize = std::exchange(other.largerSize, 0); + alignedSize = std::exchange(other.alignedSize, 0); + paddedData = std::exchange(other.paddedData, nullptr); + normalData = std::exchange(other.normalData, nullptr); + normalEnd = std::exchange(other.normalEnd, nullptr); + _alignedEnd = std::exchange(other._alignedEnd, nullptr); + } + return *this; + } + + Type &operator[](int idx) { return *(normalData + idx); } constexpr pointer data() const noexcept { return normalData; } constexpr size_type size() const noexcept { return alignedSize; } constexpr bool empty() const noexcept { return alignedSize == 0; } constexpr iterator begin() noexcept { return data(); } constexpr iterator end() noexcept { return normalEnd; } constexpr pointer alignedEnd() noexcept { return _alignedEnd; } + private: - static constexpr auto AlignmentMask { Alignment - 1 }; - static constexpr auto TypeAlignment { Alignment / sizeof(value_type) }; - static constexpr auto TypeAlignmentMask { TypeAlignment - 1 }; + static constexpr auto AlignmentMask{Alignment - 1}; + static constexpr auto TypeAlignment{Alignment / sizeof(value_type)}; + static constexpr auto TypeAlignmentMask{TypeAlignment - 1}; static_assert(std::is_arithmetic::value, "Type should be arithmetic"); static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value"); static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); - size_type largerSize { 0 }; - size_type alignedSize { 0 }; - pointer normalData { nullptr }; - pointer paddedData { nullptr }; - pointer normalEnd { nullptr }; - pointer _alignedEnd { nullptr }; + size_type largerSize{0}; + size_type alignedSize{0}; + pointer normalData{nullptr}; + pointer paddedData{nullptr}; + pointer normalEnd{nullptr}; + pointer _alignedEnd{nullptr}; }; \ No newline at end of file diff --git a/sources/StereoBuffer.h b/sources/StereoBuffer.h index b16d5f75..eae984a6 100644 --- a/sources/StereoBuffer.h +++ b/sources/StereoBuffer.h @@ -46,6 +46,8 @@ public: absl::Span getSpan(Channel channel) { switch (channel) { + default: + [[fallthrough]]; case Channel::left: return absl::MakeSpan(leftBuffer); case Channel::right: @@ -57,6 +59,8 @@ public: { ASSERT(sampleIndex >= 0); switch (channel) { + default: + [[fallthrough]]; case Channel::left: return leftBuffer[sampleIndex]; case Channel::right: diff --git a/tests/BufferT.cpp b/tests/BufferT.cpp index 5a3a22bf..f4eca35a 100644 --- a/tests/BufferT.cpp +++ b/tests/BufferT.cpp @@ -1,5 +1,5 @@ -#include "catch2/catch.hpp" #include "../sources/Buffer.h" +#include "catch2/catch.hpp" #include using namespace Catch::literals; @@ -31,7 +31,7 @@ TEST_CASE("[Buffer] Empty (uint8_t)") REQUIRE(emptyBuffer.size() == 0); } -template +template void checkBoundaries(Buffer& buffer, int expectedSize) { REQUIRE((int)buffer.size() == expectedSize); @@ -46,14 +46,13 @@ TEST_CASE("[Buffer] 10 floats ") const int baseSize { 10 }; Buffer buffer(baseSize); checkBoundaries(buffer, baseSize); - - for (auto& element: buffer) + + for (auto& element : buffer) element = 0.0f; - for (auto& element: buffer) + for (auto& element : buffer) REQUIRE(element == 0.0f); } - TEST_CASE("[Buffer] Resize 10 floats ") { const int baseSize { 10 }; @@ -62,15 +61,15 @@ TEST_CASE("[Buffer] Resize 10 floats ") Buffer buffer(baseSize); REQUIRE(!buffer.empty()); checkBoundaries(buffer, baseSize); - + std::fill(buffer.begin(), buffer.end(), 1.0f); - - REQUIRE( buffer.resize(smallSize) ); + + REQUIRE(buffer.resize(smallSize)); checkBoundaries(buffer, smallSize); - REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); - - REQUIRE( buffer.resize(bigSize) ); + REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; })); + + REQUIRE(buffer.resize(bigSize)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); @@ -84,15 +83,15 @@ TEST_CASE("[Buffer] Resize 4096 floats ") Buffer buffer(baseSize); REQUIRE(!buffer.empty()); checkBoundaries(buffer, baseSize); - + std::fill(buffer.begin(), buffer.end(), 1.0f); - - REQUIRE( buffer.resize(smallSize) ); + + REQUIRE(buffer.resize(smallSize)); checkBoundaries(buffer, smallSize); - - REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); - - REQUIRE( buffer.resize(bigSize) ); + + REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; })); + + REQUIRE(buffer.resize(bigSize)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); @@ -103,19 +102,40 @@ TEST_CASE("[Buffer] Resize 65536 floats ") const int baseSize { 10 }; const int smallSize { baseSize / 2 }; const int bigSize { baseSize * 2 }; - Buffer buffer(baseSize); + Buffer buffer(baseSize); REQUIRE(!buffer.empty()); checkBoundaries(buffer, baseSize); - + std::fill(buffer.begin(), buffer.end(), 1.0f); - - REQUIRE( buffer.resize(smallSize) ); + + REQUIRE(buffer.resize(smallSize)); checkBoundaries(buffer, smallSize); - - REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); - - REQUIRE( buffer.resize(bigSize) ); + + REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; })); + + REQUIRE(buffer.resize(bigSize)); checkBoundaries(buffer, bigSize); for (auto i = 0; i < smallSize; ++i) REQUIRE(buffer[i] == 1.0f); +} + +TEST_CASE("[Buffer] Copy and move") +{ + const int baseSize { 128 }; + Buffer buffer(baseSize); + Buffer copied { baseSize - 4 }; + std::fill(buffer.begin(), buffer.end(), 1.0f); + std::fill(copied.begin(), copied.end(), 2.0f); + copied = buffer; + checkBoundaries(copied, baseSize); + REQUIRE(std::all_of(copied.begin(), copied.end(), [](auto value) { return value == 1.0f; })); + + Buffer copyConstructed { buffer }; + checkBoundaries(copyConstructed, baseSize); + REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](auto value) { return value == 1.0f; })); + + Buffer moveConstructed { std::move(buffer) }; + REQUIRE(buffer.empty()); + checkBoundaries(moveConstructed, baseSize); + REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; })); } \ No newline at end of file