Added copy and move to Buffer

This commit is contained in:
paulfd 2019-08-24 11:32:19 +02:00
parent 63beae0b2a
commit 3598d45c26
3 changed files with 121 additions and 45 deletions

View file

@ -1,18 +1,20 @@
#pragma once #pragma once
#include "Globals.h" #include "Globals.h"
#include <type_traits>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <memory> #include <memory>
#include <type_traits>
#include <utility>
template<class Type, unsigned int Alignment = SIMDConfig::defaultAlignment> template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
class Buffer class Buffer
{ {
public: public:
using value_type = std::remove_cv_t<Type>; using value_type = std::remove_cv_t<Type>;
using pointer = value_type*; using pointer = value_type *;
using const_pointer = const value_type*; using const_pointer = const value_type *;
using reference = value_type&; using reference = value_type &;
using const_reference = const value_type&; using const_reference = const value_type &;
using iterator = pointer; using iterator = pointer;
using const_iterator = const_pointer; using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
@ -20,7 +22,9 @@ public:
using size_type = size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
constexpr Buffer() { } Buffer()
{
}
Buffer(size_t size) Buffer(size_t size)
{ {
resize(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 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) if (newData == nullptr)
{
return false; return false;
}
largerSize = tempSize; largerSize = tempSize;
alignedSize = newSize; alignedSize = newSize;
@ -47,6 +53,8 @@ public:
_alignedEnd = normalEnd + Alignment - endMisalignment; _alignedEnd = normalEnd + Alignment - endMisalignment;
else else
_alignedEnd = normalEnd; _alignedEnd = normalEnd;
DBG("Buffer resized (" << newSize << ") at: " << this);
return true; return true;
} }
@ -64,24 +72,68 @@ public:
std::free(paddedData); std::free(paddedData);
} }
Type& operator[](int idx) { return *(normalData + idx); } Buffer(const Buffer<Type> &other)
{
if (resize(other.size()))
{
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
}
}
Buffer(Buffer<Type> &&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<Type> &operator=(const Buffer<Type> &other)
{
if (this != &other)
{
if (resize(other.size()))
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
}
return *this;
}
Buffer<Type> &operator=(Buffer<Type> &&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 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; }
constexpr iterator begin() noexcept { return data(); } constexpr iterator begin() noexcept { return data(); }
constexpr iterator end() noexcept { return normalEnd; } constexpr iterator end() noexcept { return normalEnd; }
constexpr pointer alignedEnd() noexcept { return _alignedEnd; } constexpr pointer alignedEnd() noexcept { return _alignedEnd; }
private: private:
static constexpr auto AlignmentMask { Alignment - 1 }; static constexpr auto AlignmentMask{Alignment - 1};
static constexpr auto TypeAlignment { Alignment / sizeof(value_type) }; static constexpr auto TypeAlignment{Alignment / sizeof(value_type)};
static constexpr auto TypeAlignmentMask { TypeAlignment - 1 }; static constexpr auto TypeAlignmentMask{TypeAlignment - 1};
static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic"); static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic");
static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value"); 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"); 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 largerSize{0};
size_type alignedSize { 0 }; size_type alignedSize{0};
pointer normalData { nullptr }; pointer normalData{nullptr};
pointer paddedData { nullptr }; pointer paddedData{nullptr};
pointer normalEnd { nullptr }; pointer normalEnd{nullptr};
pointer _alignedEnd { nullptr }; pointer _alignedEnd{nullptr};
}; };

View file

@ -46,6 +46,8 @@ public:
absl::Span<Type> getSpan(Channel channel) absl::Span<Type> getSpan(Channel channel)
{ {
switch (channel) { switch (channel) {
default:
[[fallthrough]];
case Channel::left: case Channel::left:
return absl::MakeSpan(leftBuffer); return absl::MakeSpan(leftBuffer);
case Channel::right: case Channel::right:
@ -57,6 +59,8 @@ public:
{ {
ASSERT(sampleIndex >= 0); ASSERT(sampleIndex >= 0);
switch (channel) { switch (channel) {
default:
[[fallthrough]];
case Channel::left: case Channel::left:
return leftBuffer[sampleIndex]; return leftBuffer[sampleIndex];
case Channel::right: case Channel::right:

View file

@ -1,5 +1,5 @@
#include "catch2/catch.hpp"
#include "../sources/Buffer.h" #include "../sources/Buffer.h"
#include "catch2/catch.hpp"
#include <algorithm> #include <algorithm>
using namespace Catch::literals; using namespace Catch::literals;
@ -31,7 +31,7 @@ TEST_CASE("[Buffer] Empty (uint8_t)")
REQUIRE(emptyBuffer.size() == 0); REQUIRE(emptyBuffer.size() == 0);
} }
template<class Type> template <class Type>
void checkBoundaries(Buffer<Type>& buffer, int expectedSize) void checkBoundaries(Buffer<Type>& buffer, int expectedSize)
{ {
REQUIRE((int)buffer.size() == expectedSize); REQUIRE((int)buffer.size() == expectedSize);
@ -46,14 +46,13 @@ TEST_CASE("[Buffer] 10 floats ")
const int baseSize { 10 }; const int baseSize { 10 };
Buffer<float> buffer(baseSize); Buffer<float> buffer(baseSize);
checkBoundaries(buffer, baseSize); checkBoundaries(buffer, baseSize);
for (auto& element: buffer) for (auto& element : buffer)
element = 0.0f; element = 0.0f;
for (auto& element: buffer) for (auto& element : buffer)
REQUIRE(element == 0.0f); REQUIRE(element == 0.0f);
} }
TEST_CASE("[Buffer] Resize 10 floats ") TEST_CASE("[Buffer] Resize 10 floats ")
{ {
const int baseSize { 10 }; const int baseSize { 10 };
@ -62,15 +61,15 @@ TEST_CASE("[Buffer] Resize 10 floats ")
Buffer<float> buffer(baseSize); Buffer<float> buffer(baseSize);
REQUIRE(!buffer.empty()); REQUIRE(!buffer.empty());
checkBoundaries(buffer, baseSize); checkBoundaries(buffer, baseSize);
std::fill(buffer.begin(), buffer.end(), 1.0f); std::fill(buffer.begin(), buffer.end(), 1.0f);
REQUIRE( buffer.resize(smallSize) ); REQUIRE(buffer.resize(smallSize));
checkBoundaries(buffer, smallSize); checkBoundaries(buffer, smallSize);
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }));
REQUIRE( buffer.resize(bigSize) ); REQUIRE(buffer.resize(bigSize));
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);
@ -84,15 +83,15 @@ TEST_CASE("[Buffer] Resize 4096 floats ")
Buffer<float> buffer(baseSize); Buffer<float> buffer(baseSize);
REQUIRE(!buffer.empty()); REQUIRE(!buffer.empty());
checkBoundaries(buffer, baseSize); checkBoundaries(buffer, baseSize);
std::fill(buffer.begin(), buffer.end(), 1.0f); std::fill(buffer.begin(), buffer.end(), 1.0f);
REQUIRE( buffer.resize(smallSize) ); REQUIRE(buffer.resize(smallSize));
checkBoundaries(buffer, smallSize); checkBoundaries(buffer, smallSize);
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }));
REQUIRE( buffer.resize(bigSize) ); REQUIRE(buffer.resize(bigSize));
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);
@ -103,19 +102,40 @@ TEST_CASE("[Buffer] Resize 65536 floats ")
const int baseSize { 10 }; const int baseSize { 10 };
const int smallSize { baseSize / 2 }; const int smallSize { baseSize / 2 };
const int bigSize { baseSize * 2 }; const int bigSize { baseSize * 2 };
Buffer<float> buffer(baseSize); Buffer<float> buffer(baseSize);
REQUIRE(!buffer.empty()); REQUIRE(!buffer.empty());
checkBoundaries(buffer, baseSize); checkBoundaries(buffer, baseSize);
std::fill(buffer.begin(), buffer.end(), 1.0f); std::fill(buffer.begin(), buffer.end(), 1.0f);
REQUIRE( buffer.resize(smallSize) ); REQUIRE(buffer.resize(smallSize));
checkBoundaries(buffer, smallSize); checkBoundaries(buffer, smallSize);
REQUIRE( std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }) ); REQUIRE(std::all_of(buffer.begin(), buffer.end(), [](auto value) { return value == 1.0f; }));
REQUIRE( buffer.resize(bigSize) ); REQUIRE(buffer.resize(bigSize));
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);
}
TEST_CASE("[Buffer] Copy and move")
{
const int baseSize { 128 };
Buffer<float> buffer(baseSize);
Buffer<float> 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<float> copyConstructed { buffer };
checkBoundaries(copyConstructed, baseSize);
REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](auto value) { return value == 1.0f; }));
Buffer<float> moveConstructed { std::move(buffer) };
REQUIRE(buffer.empty());
checkBoundaries(moveConstructed, baseSize);
REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; }));
} }