Added copy and move to Buffer
This commit is contained in:
parent
63beae0b2a
commit
3598d45c26
3 changed files with 121 additions and 45 deletions
|
|
@ -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};
|
||||||
};
|
};
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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; }));
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue