juicysfplugin/modules/juce_core/containers/juce_ArrayBase.cpp

601 lines
23 KiB
C++
Raw Normal View History

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2018 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
namespace ArrayBaseTestsHelpers
{
class TriviallyCopyableType
{
public:
TriviallyCopyableType() = default;
TriviallyCopyableType (int v)
: value (v)
{}
TriviallyCopyableType (float v)
: value ((int) v)
{}
bool operator== (const TriviallyCopyableType& other) const
{
return getValue() == other.getValue();
}
int getValue() const { return value; }
private:
int value { -1111 };
};
class NonTriviallyCopyableType
{
public:
NonTriviallyCopyableType() = default;
NonTriviallyCopyableType (int v)
: value (v)
{}
NonTriviallyCopyableType (float v)
: value ((int) v)
{}
NonTriviallyCopyableType (const NonTriviallyCopyableType& other)
: value (other.value)
{}
NonTriviallyCopyableType& operator= (const NonTriviallyCopyableType& other)
{
value = other.value;
return *this;
}
bool operator== (const NonTriviallyCopyableType& other) const
{
return getValue() == other.getValue();
}
int getValue() const { return *ptr; }
private:
int value { -1111 };
int* ptr = &value;
};
}
bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
{
return tct.getValue() == ntct.getValue();
}
bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct,
const ArrayBaseTestsHelpers::TriviallyCopyableType& tct)
{
return tct == ntct;
}
class ArrayBaseTests : public UnitTest
{
using CopyableType = ArrayBaseTestsHelpers::TriviallyCopyableType;
using NoncopyableType = ArrayBaseTestsHelpers::NonTriviallyCopyableType;
#if ! (defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__))
static_assert (std::is_trivially_copyable<CopyableType>::value,
"Test TriviallyCopyableType is not trivially copyable");
static_assert (! std::is_trivially_copyable<NoncopyableType>::value,
"Test NonTriviallyCopyableType is trivially copyable");
#endif
public:
ArrayBaseTests()
: UnitTest ("ArrayBase", "Containers")
{}
void runTest() override
{
beginTest ("grow capacity");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
int originalCapacity = 4;
referenceContainer.reserve ((size_t) originalCapacity);
expectEquals ((int) referenceContainer.capacity(), originalCapacity);
copyableContainer.setAllocatedSize (originalCapacity);
expectEquals (copyableContainer.capacity(), originalCapacity);
noncopyableContainer.setAllocatedSize (originalCapacity);
expectEquals (noncopyableContainer.capacity(), originalCapacity);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, 33);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
expect ((int) referenceContainer.capacity() != originalCapacity);
expect (copyableContainer.capacity() != originalCapacity);
expect (noncopyableContainer.capacity() != originalCapacity);
}
beginTest ("shrink capacity");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
int numElements = 45;
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
copyableContainer.shrinkToNoMoreThan (numElements);
noncopyableContainer.setAllocatedSize (numElements + 1);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
referenceContainer.clear();
copyableContainer.removeElements (0, numElements);
noncopyableContainer.removeElements (0, numElements);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
copyableContainer.setAllocatedSize (0);
noncopyableContainer.setAllocatedSize (0);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("equality");
{
std::vector<int> referenceContainer = { 1, 2, 3 };
ArrayBase<int, DummyCriticalSection> testContainer1, testContainer2;
for (auto i : referenceContainer)
{
testContainer1.add (i);
testContainer2.add (i);
}
expect (testContainer1 == referenceContainer);
expect (testContainer2 == testContainer1);
testContainer1.ensureAllocatedSize (257);
referenceContainer.shrink_to_fit();
expect (testContainer1 == referenceContainer);
expect (testContainer2 == testContainer1);
testContainer1.removeElements (0, 1);
expect (testContainer1 != referenceContainer);
expect (testContainer2 != testContainer1);
}
beginTest ("accessors");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 3);
int testValue = -123;
referenceContainer[0] = testValue;
copyableContainer[0] = testValue;
noncopyableContainer[0] = testValue;
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
expect (copyableContainer .getFirst().getValue() == testValue);
expect (noncopyableContainer.getFirst().getValue() == testValue);
auto last = referenceContainer.back().getValue();
expectEquals (copyableContainer .getLast().getValue(), last);
expectEquals (noncopyableContainer.getLast().getValue(), last);
ArrayBase<CopyableType, DummyCriticalSection> copyableEmpty;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableEmpty;
auto defualtValue = CopyableType().getValue();
expectEquals (defualtValue, NoncopyableType().getValue());
expectEquals (copyableEmpty .getFirst().getValue(), defualtValue);
expectEquals (noncopyableEmpty.getFirst().getValue(), defualtValue);
expectEquals (copyableEmpty .getLast() .getValue(), defualtValue);
expectEquals (noncopyableEmpty.getLast() .getValue(), defualtValue);
ArrayBase<float*, DummyCriticalSection> floatPointers;
expect (floatPointers.getValueWithDefault (-3) == nullptr);
}
beginTest ("add moved");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
for (int i = 0; i < 5; ++i)
{
CopyableType ref (-i);
CopyableType ct (-i);
NoncopyableType nct (-i);
referenceContainer.push_back (std::move (ref));
copyableContainer.add (std::move (ct));
noncopyableContainer.add (std::move (nct));
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add multiple");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
for (int i = 4; i < 7; ++i)
referenceContainer.push_back ({ -i });
copyableContainer.add (CopyableType (-4), CopyableType (-5), CopyableType (-6));
noncopyableContainer.add (NoncopyableType (-4), NoncopyableType (-5), NoncopyableType (-6));
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from a pointer");
{
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<CopyableType> copyableData = { 3, 4, 5 };
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5 };
copyableContainer.addArray (copyableData.data(), (int) copyableData.size());
noncopyableContainer.addArray (noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, copyableData);
}
beginTest ("add array from a pointer of a different type");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<float> floatData = { 1.4f, 2.5f, 3.6f };
for (auto f : floatData)
referenceContainer.push_back ({ f });
copyableContainer.addArray (floatData.data(), (int) floatData.size());
noncopyableContainer.addArray (floatData.data(), (int) floatData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from initilizer list");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::initializer_list<CopyableType> ilct { { 3 }, { 4 }, { 5 } };
std::initializer_list<NoncopyableType> ilnct { { 3 }, { 4 }, { 5 } };
for (auto v : ilct)
referenceContainer.push_back ({ v });
copyableContainer.addArray (ilct);
noncopyableContainer.addArray (ilnct);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from containers");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 5);
std::vector<CopyableType> referenceContainerCopy (referenceContainer);
std::vector<NoncopyableType> noncopyableReferenceContainerCopy;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainerCopy;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainerCopy;
for (auto& v : referenceContainerCopy)
noncopyableReferenceContainerCopy.push_back ({ v.getValue() });
for (size_t i = 0; i < referenceContainerCopy.size(); ++i)
{
auto value = referenceContainerCopy[i].getValue();
copyableContainerCopy.add ({ value });
noncopyableContainerCopy.add ({ value });
}
// From self-types
copyableContainer.addArray (copyableContainerCopy);
noncopyableContainer.addArray (noncopyableContainerCopy);
for (auto v : referenceContainerCopy)
referenceContainer.push_back (v);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
// From std containers
copyableContainer.addArray (referenceContainerCopy);
noncopyableContainer.addArray (noncopyableReferenceContainerCopy);
for (auto v : referenceContainerCopy)
referenceContainer.push_back (v);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
// From std containers with offset
int offset = 5;
copyableContainer.addArray (referenceContainerCopy, offset);
noncopyableContainer.addArray (noncopyableReferenceContainerCopy, offset);
for (size_t i = 5; i < referenceContainerCopy.size(); ++i)
referenceContainer.push_back (referenceContainerCopy[i]);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("insert");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 8);
referenceContainer.insert (referenceContainer.begin(), -4);
copyableContainer.insert (0, -4, 1);
noncopyableContainer.insert (0, -4, 1);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
for (int i = 0; i < 3; ++i)
referenceContainer.insert (referenceContainer.begin() + 1, -3);
copyableContainer.insert (1, -3, 3);
noncopyableContainer.insert (1, -3, 3);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
for (int i = 0; i < 50; ++i)
referenceContainer.insert (referenceContainer.end() - 1, -9);
copyableContainer.insert (copyableContainer.size() - 2, -9, 50);
noncopyableContainer.insert (noncopyableContainer.size() - 2, -9, 50);
}
beginTest ("insert array");
{
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<CopyableType> copyableData = { 3, 4, 5, 6, 7, 8 };
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5, 6, 7, 8 };
std::vector<CopyableType> referenceContainer { copyableData };
copyableContainer.insertArray (0, copyableData.data(), (int) copyableData.size());
noncopyableContainer.insertArray (0, noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
int insertPos = copyableContainer.size() - 1;
for (auto it = copyableData.end(); it != copyableData.begin(); --it)
referenceContainer.insert (referenceContainer.begin() + insertPos, CopyableType (*(it - 1)));
copyableContainer.insertArray (insertPos, copyableData.data(), (int) copyableData.size());
noncopyableContainer.insertArray (insertPos, noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("remove");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
for (int i = 0; i < 4; ++i)
{
referenceContainer.erase (referenceContainer.begin() + i);
copyableContainer.removeElements (i, 1);
noncopyableContainer.removeElements (i, 1);
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
int blockSize = 3;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < blockSize; ++j)
referenceContainer.erase (referenceContainer.begin() + i);
copyableContainer.removeElements (i, blockSize);
noncopyableContainer.removeElements (i, blockSize);
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
auto numToRemove = copyableContainer.size() - 2;
for (int i = 0; i < numToRemove; ++i)
referenceContainer.erase (referenceContainer.begin() + 1);
copyableContainer.removeElements (1, numToRemove);
noncopyableContainer.removeElements (1, numToRemove);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("move");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 6);
std::vector<std::pair<int, int>> testValues;
testValues.emplace_back (2, 4);
testValues.emplace_back (0, 5);
testValues.emplace_back (4, 1);
testValues.emplace_back (5, 0);
for (auto p : testValues)
{
if (p.second > p.first)
std::rotate (referenceContainer.begin() + p.first,
referenceContainer.begin() + p.first + 1,
referenceContainer.begin() + p.second + 1);
else
std::rotate (referenceContainer.begin() + p.second,
referenceContainer.begin() + p.first,
referenceContainer.begin() + p.first + 1);
copyableContainer.move (p.first, p.second);
noncopyableContainer.move (p.first, p.second);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
}
beginTest ("After converting move construction, ownership is transferred");
{
Derived obj;
ArrayBase<Derived*, DummyCriticalSection> derived;
derived.setAllocatedSize (5);
derived.add (&obj);
ArrayBase<Base*, DummyCriticalSection> base { std::move (derived) };
expectEquals (base.capacity(), 5);
expectEquals (base.size(), 1);
expect (base.getFirst() == &obj);
expectEquals (derived.capacity(), 0);
expectEquals (derived.size(), 0);
expect (derived.data() == nullptr);
}
beginTest ("After converting move assignment, ownership is transferred");
{
Derived obj;
ArrayBase<Derived*, DummyCriticalSection> derived;
derived.setAllocatedSize (5);
derived.add (&obj);
ArrayBase<Base*, DummyCriticalSection> base;
base = std::move (derived);
expectEquals (base.capacity(), 5);
expectEquals (base.size(), 1);
expect (base.getFirst() == &obj);
expectEquals (derived.capacity(), 0);
expectEquals (derived.size(), 0);
expect (derived.data() == nullptr);
}
}
private:
struct Base
{
virtual ~Base() = default;
};
struct Derived : Base
{
};
static void addData (std::vector<CopyableType>& referenceContainer,
ArrayBase<CopyableType, DummyCriticalSection>& copyableContainer,
ArrayBase<NoncopyableType, DummyCriticalSection>& NoncopyableContainer,
int numValues)
{
for (int i = 0; i < numValues; ++i)
{
referenceContainer.push_back ({ i });
copyableContainer.add ({ i });
NoncopyableContainer.add ({ i });
}
}
template<typename A, typename B>
void checkEqual (const ArrayBase<A, DummyCriticalSection>& a,
const ArrayBase<B, DummyCriticalSection>& b)
{
expectEquals ((int) a.size(), (int) b.size());
for (int i = 0; i < (int) a.size(); ++i)
expect (a[i] == b[i]);
}
template<typename A, typename B>
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
std::vector<B>& b)
{
expectEquals ((int) a.size(), (int) b.size());
for (int i = 0; i < (int) a.size(); ++i)
expect (a[i] == b[(size_t) i]);
}
template<typename A, typename B, typename C>
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
ArrayBase<B, DummyCriticalSection>& b,
std::vector<C>& c)
{
checkEqual (a, b);
checkEqual (a, c);
checkEqual (b, c);
}
};
static ArrayBaseTests arrayBaseTests;
#endif
} // namespace juce